Home | History | Annotate | Download | only in runtime
      1 // Copyright 2017 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 runtime_test
      6 
      7 import (
      8 	"reflect"
      9 	. "runtime"
     10 	"testing"
     11 	"time"
     12 	"unsafe"
     13 )
     14 
     15 func TestProfBuf(t *testing.T) {
     16 	const hdrSize = 2
     17 
     18 	write := func(t *testing.T, b *ProfBuf, tag unsafe.Pointer, now int64, hdr []uint64, stk []uintptr) {
     19 		b.Write(&tag, now, hdr, stk)
     20 	}
     21 	read := func(t *testing.T, b *ProfBuf, data []uint64, tags []unsafe.Pointer) {
     22 		rdata, rtags, eof := b.Read(ProfBufNonBlocking)
     23 		if !reflect.DeepEqual(rdata, data) || !reflect.DeepEqual(rtags, tags) {
     24 			t.Fatalf("unexpected profile read:\nhave data %#x\nwant data %#x\nhave tags %#x\nwant tags %#x", rdata, data, rtags, tags)
     25 		}
     26 		if eof {
     27 			t.Fatalf("unexpected eof")
     28 		}
     29 	}
     30 	readBlock := func(t *testing.T, b *ProfBuf, data []uint64, tags []unsafe.Pointer) func() {
     31 		c := make(chan int)
     32 		go func() {
     33 			eof := data == nil
     34 			rdata, rtags, reof := b.Read(ProfBufBlocking)
     35 			if !reflect.DeepEqual(rdata, data) || !reflect.DeepEqual(rtags, tags) || reof != eof {
     36 				// Errorf, not Fatalf, because called in goroutine.
     37 				t.Errorf("unexpected profile read:\nhave data %#x\nwant data %#x\nhave tags %#x\nwant tags %#x\nhave eof=%v, want %v", rdata, data, rtags, tags, reof, eof)
     38 			}
     39 			c <- 1
     40 		}()
     41 		time.Sleep(10 * time.Millisecond) // let goroutine run and block
     42 		return func() {
     43 			select {
     44 			case <-c:
     45 			case <-time.After(1 * time.Second):
     46 				t.Fatalf("timeout waiting for blocked read")
     47 			}
     48 		}
     49 	}
     50 	readEOF := func(t *testing.T, b *ProfBuf) {
     51 		rdata, rtags, eof := b.Read(ProfBufBlocking)
     52 		if rdata != nil || rtags != nil || !eof {
     53 			t.Errorf("unexpected profile read: %#x, %#x, eof=%v; want nil, nil, eof=true", rdata, rtags, eof)
     54 		}
     55 		rdata, rtags, eof = b.Read(ProfBufNonBlocking)
     56 		if rdata != nil || rtags != nil || !eof {
     57 			t.Errorf("unexpected profile read (non-blocking): %#x, %#x, eof=%v; want nil, nil, eof=true", rdata, rtags, eof)
     58 		}
     59 	}
     60 
     61 	myTags := make([]byte, 100)
     62 	t.Logf("myTags is %p", &myTags[0])
     63 
     64 	t.Run("BasicWriteRead", func(t *testing.T) {
     65 		b := NewProfBuf(2, 11, 1)
     66 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
     67 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
     68 		read(t, b, nil, nil) // release data returned by previous read
     69 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
     70 		read(t, b, []uint64{8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[2])})
     71 	})
     72 
     73 	t.Run("ReadMany", func(t *testing.T) {
     74 		b := NewProfBuf(2, 50, 50)
     75 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
     76 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
     77 		write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506})
     78 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204, 5, 500, 502, 504, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2]), unsafe.Pointer(&myTags[1])})
     79 	})
     80 
     81 	t.Run("ReadManyShortData", func(t *testing.T) {
     82 		b := NewProfBuf(2, 50, 50)
     83 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
     84 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
     85 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2])})
     86 	})
     87 
     88 	t.Run("ReadManyShortTags", func(t *testing.T) {
     89 		b := NewProfBuf(2, 50, 50)
     90 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
     91 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
     92 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2])})
     93 	})
     94 
     95 	t.Run("ReadAfterOverflow1", func(t *testing.T) {
     96 		// overflow record synthesized by write
     97 		b := NewProfBuf(2, 16, 5)
     98 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})           // uses 10
     99 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])}) // reads 10 but still in use until next read
    100 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5})                       // uses 6
    101 		read(t, b, []uint64{6, 1, 2, 3, 4, 5}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})              // reads 6 but still in use until next read
    102 		// now 10 available
    103 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204, 205, 206, 207, 208, 209}) // no room
    104 		for i := 0; i < 299; i++ {
    105 			write(t, b, unsafe.Pointer(&myTags[3]), int64(100+i), []uint64{101, 102}, []uintptr{201, 202, 203, 204}) // no room for overflow+this record
    106 		}
    107 		write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506}) // room for overflow+this record
    108 		read(t, b, []uint64{5, 99, 0, 0, 300, 5, 500, 502, 504, 506}, []unsafe.Pointer{nil, unsafe.Pointer(&myTags[1])})
    109 	})
    110 
    111 	t.Run("ReadAfterOverflow2", func(t *testing.T) {
    112 		// overflow record synthesized by read
    113 		b := NewProfBuf(2, 16, 5)
    114 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    115 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213})
    116 		for i := 0; i < 299; i++ {
    117 			write(t, b, unsafe.Pointer(&myTags[3]), 100, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
    118 		}
    119 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])}) // reads 10 but still in use until next read
    120 		write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{})                     // still overflow
    121 		read(t, b, []uint64{5, 99, 0, 0, 301}, []unsafe.Pointer{nil})                                     // overflow synthesized by read
    122 		write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 505}, []uintptr{506})                  // written
    123 		read(t, b, []uint64{5, 500, 502, 505, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[1])})
    124 	})
    125 
    126 	t.Run("ReadAtEndAfterOverflow", func(t *testing.T) {
    127 		b := NewProfBuf(2, 12, 5)
    128 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    129 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
    130 		for i := 0; i < 299; i++ {
    131 			write(t, b, unsafe.Pointer(&myTags[3]), 100, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
    132 		}
    133 		read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
    134 		read(t, b, []uint64{5, 99, 0, 0, 300}, []unsafe.Pointer{nil})
    135 		write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506})
    136 		read(t, b, []uint64{5, 500, 502, 504, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[1])})
    137 	})
    138 
    139 	t.Run("BlockingWriteRead", func(t *testing.T) {
    140 		b := NewProfBuf(2, 11, 1)
    141 		wait := readBlock(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
    142 		write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    143 		wait()
    144 		wait = readBlock(t, b, []uint64{8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[2])})
    145 		time.Sleep(10 * time.Millisecond)
    146 		write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
    147 		wait()
    148 		wait = readBlock(t, b, nil, nil)
    149 		b.Close()
    150 		wait()
    151 		wait = readBlock(t, b, nil, nil)
    152 		wait()
    153 		readEOF(t, b)
    154 	})
    155 
    156 	t.Run("DataWraparound", func(t *testing.T) {
    157 		b := NewProfBuf(2, 16, 1024)
    158 		for i := 0; i < 10; i++ {
    159 			write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    160 			read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
    161 			read(t, b, nil, nil) // release data returned by previous read
    162 		}
    163 	})
    164 
    165 	t.Run("TagWraparound", func(t *testing.T) {
    166 		b := NewProfBuf(2, 1024, 2)
    167 		for i := 0; i < 10; i++ {
    168 			write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    169 			read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
    170 			read(t, b, nil, nil) // release data returned by previous read
    171 		}
    172 	})
    173 
    174 	t.Run("BothWraparound", func(t *testing.T) {
    175 		b := NewProfBuf(2, 16, 2)
    176 		for i := 0; i < 10; i++ {
    177 			write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
    178 			read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
    179 			read(t, b, nil, nil) // release data returned by previous read
    180 		}
    181 	})
    182 }
    183