github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/pkg/pool/virtualmem/virtualmem_test.go (about)

     1  // Copyright (c) 2023 Paweł Gaczyński
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package virtualmem
    16  
    17  import (
    18  	"crypto/rand"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"log"
    22  	"os"
    23  	"os/exec"
    24  	"reflect"
    25  	"syscall"
    26  	"testing"
    27  	"unsafe"
    28  
    29  	. "github.com/stretchr/testify/require"
    30  )
    31  
    32  func deallocate(data []byte, size int) error {
    33  	return internalMunmap(uintptr(unsafe.Pointer(&data[0])), size*2)
    34  }
    35  
    36  func allocateTempfileBuffer(size int) []byte {
    37  	nofd := ^uintptr(0)
    38  
    39  	vaddr, err := internalMmap(
    40  		0, 2*size, syscall.MAP_SHARED|syscall.MAP_ANONYMOUS, nofd)
    41  	if err != nil {
    42  		log.Panic(err)
    43  	}
    44  
    45  	file, err := ioutil.TempFile("", "magicbuffer")
    46  	if err != nil {
    47  		log.Panic(err)
    48  	}
    49  	defer file.Close()
    50  
    51  	if err = os.Remove(file.Name()); err != nil {
    52  		log.Panic(err)
    53  	}
    54  
    55  	fd := file.Fd()
    56  	if err = syscall.Ftruncate(int(fd), int64(size)); err != nil {
    57  		log.Panic(err)
    58  	}
    59  
    60  	_, err = internalMmap(vaddr, size, syscall.MAP_SHARED|syscall.MAP_FIXED, fd)
    61  	if err != nil {
    62  		log.Panic(fmt.Errorf("first internal mmap failed: %w", err))
    63  	}
    64  
    65  	_, err = internalMmap(
    66  		vaddr+uintptr(size), size, syscall.MAP_SHARED|syscall.MAP_FIXED, fd)
    67  	if err != nil {
    68  		log.Panic(fmt.Errorf("second internal mmap failed: %w", err))
    69  	}
    70  
    71  	sliceHeader := reflect.SliceHeader{
    72  		Data: vaddr,
    73  		Len:  2 * size,
    74  		Cap:  2 * size,
    75  	}
    76  	buf := *(*[]byte)(unsafe.Pointer(&sliceHeader)) //nolint:govet
    77  
    78  	return buf
    79  }
    80  
    81  func BenchmarkVMTempFile(b *testing.B) {
    82  	var (
    83  		pagesize = os.Getpagesize()
    84  		err      error
    85  	)
    86  
    87  	for i := 0; i < b.N; i++ {
    88  		data := allocateTempfileBuffer(pagesize)
    89  		if err = deallocate(data, pagesize); err != nil {
    90  			log.Panic(err)
    91  		}
    92  	}
    93  }
    94  
    95  func BenchmarkVMMemfd(b *testing.B) {
    96  	var (
    97  		pagesize = os.Getpagesize()
    98  		err      error
    99  	)
   100  
   101  	for i := 0; i < b.N; i++ {
   102  		data := allocateBuffer(pagesize)
   103  		if err = deallocate(data, pagesize); err != nil {
   104  			log.Panic(err)
   105  		}
   106  	}
   107  }
   108  
   109  var printMapsAndFds = os.Getenv("TEST_PRINT_MAPS_AND_FDS") == "true"
   110  
   111  var printCmd = func(label, cmd string) {
   112  	out, err := exec.Command("bash", "-c", cmd).Output()
   113  	if err != nil {
   114  		log.Panic(err)
   115  	}
   116  
   117  	if printMapsAndFds {
   118  		fmt.Printf("%s: %s\n", label, string(out)) //nolint:forbidigo
   119  	}
   120  }
   121  
   122  func TestMemfdMMapAndUnmap(_ *testing.T) {
   123  	pagesize := os.Getpagesize()
   124  	pid := os.Getpid()
   125  	listCmd := fmt.Sprintf("cat /proc/%d/maps", pid)
   126  	countCmd := fmt.Sprintf("cat /proc/%d/maps | wc -l", pid)
   127  	fdCmd := fmt.Sprintf("ls -l /proc/%d/fd | wc -l", pid)
   128  
   129  	printCmd("ulimit -a", "ulimit -a")
   130  	printCmd("max map count", "sysctl vm.max_map_count")
   131  
   132  	printList := func() {
   133  		printCmd("list of proc maps", listCmd)
   134  	}
   135  	printCount := func() {
   136  		printCmd("count of proc maps", countCmd)
   137  	}
   138  	printFds := func() {
   139  		printCmd("count of proc fds", fdCmd)
   140  	}
   141  
   142  	var (
   143  		data []byte
   144  		err  error
   145  	)
   146  
   147  	printCount()
   148  	printFds()
   149  
   150  	for i := 0; i < 1; i++ {
   151  		data = allocateBuffer(pagesize)
   152  		if err = deallocate(data, pagesize); err != nil {
   153  			log.Panic(err)
   154  		}
   155  	}
   156  
   157  	printCount()
   158  	printFds()
   159  
   160  	for i := 0; i < 100; i++ {
   161  		data = allocateBuffer(pagesize)
   162  		if err = deallocate(data, pagesize); err != nil {
   163  			log.Panic(err)
   164  		}
   165  	}
   166  
   167  	printCount()
   168  	printFds()
   169  
   170  	for i := 0; i < 1000; i++ {
   171  		data = allocateBuffer(pagesize)
   172  		if err = deallocate(data, pagesize); err != nil {
   173  			log.Panic(err)
   174  		}
   175  	}
   176  
   177  	printCount()
   178  	printFds()
   179  
   180  	for i := 0; i < 10000; i++ {
   181  		data = allocateBuffer(pagesize)
   182  		if err = deallocate(data, pagesize); err != nil {
   183  			log.Panic(err)
   184  		}
   185  	}
   186  
   187  	printCount()
   188  	printFds()
   189  
   190  	for i := 0; i < 10000; i++ {
   191  		data = allocateBuffer(pagesize)
   192  		if err = deallocate(data, pagesize); err != nil {
   193  			log.Panic(err)
   194  		}
   195  	}
   196  
   197  	printCount()
   198  	printFds()
   199  
   200  	for i := 0; i < 10000; i++ {
   201  		data = allocateBuffer(pagesize)
   202  		if err = deallocate(data, pagesize); err != nil {
   203  			log.Panic(err)
   204  		}
   205  	}
   206  
   207  	printCount()
   208  	printFds()
   209  
   210  	for i := 0; i < 10000; i++ {
   211  		data = allocateBuffer(pagesize)
   212  		if err = deallocate(data, pagesize); err != nil {
   213  			log.Panic(err)
   214  		}
   215  	}
   216  
   217  	printCount()
   218  	printFds()
   219  
   220  	for i := 0; i < 10000; i++ {
   221  		data = allocateBuffer(pagesize)
   222  		if err = deallocate(data, pagesize); err != nil {
   223  			log.Panic(err)
   224  		}
   225  	}
   226  
   227  	printCount()
   228  	printFds()
   229  
   230  	for i := 0; i < 10000; i++ {
   231  		data = allocateBuffer(pagesize)
   232  		if err = deallocate(data, pagesize); err != nil {
   233  			log.Panic(err)
   234  		}
   235  	}
   236  
   237  	printCount()
   238  	printFds()
   239  
   240  	for i := 0; i < 10000; i++ {
   241  		data = allocateBuffer(pagesize)
   242  		if err = deallocate(data, pagesize); err != nil {
   243  			log.Panic(err)
   244  		}
   245  	}
   246  
   247  	printCount()
   248  	printFds()
   249  
   250  	for i := 0; i < 10000; i++ {
   251  		data = allocateBuffer(pagesize)
   252  		if err = deallocate(data, pagesize); err != nil {
   253  			log.Panic(err)
   254  		}
   255  	}
   256  
   257  	printCount()
   258  	printFds()
   259  
   260  	for i := 0; i < 10000; i++ {
   261  		data = allocateBuffer(pagesize)
   262  		if err = deallocate(data, pagesize); err != nil {
   263  			log.Panic(err)
   264  		}
   265  	}
   266  
   267  	printCount()
   268  	printFds()
   269  
   270  	for i := 0; i < 30000; i++ {
   271  		data = allocateBuffer(pagesize)
   272  		if err = deallocate(data, pagesize); err != nil {
   273  			log.Panic(err)
   274  		}
   275  	}
   276  
   277  	printCount()
   278  	printFds()
   279  
   280  	for i := 0; i < 60000; i++ {
   281  		data = allocateBuffer(pagesize)
   282  		if err = deallocate(data, pagesize); err != nil {
   283  			log.Panic(err)
   284  		}
   285  	}
   286  
   287  	printCount()
   288  	printFds()
   289  	printList()
   290  }
   291  
   292  func TestVirtualMem(t *testing.T) {
   293  	vm := NewVirtualMem(os.Getpagesize())
   294  	NotNil(t, vm)
   295  	Equal(t, os.Getpagesize(), vm.Size)
   296  
   297  	Equal(t, os.Getpagesize()*2, len(vm.Buf))
   298  
   299  	dataSize := os.Getpagesize()
   300  	data := make([]byte, dataSize)
   301  	_, err := rand.Read(data)
   302  	Nil(t, err)
   303  
   304  	for i := range vm.Buf {
   305  		Zero(t, vm.Buf[i])
   306  	}
   307  
   308  	for i := 0; i < vm.Size; i++ {
   309  		vm.Buf[i] = data[i]
   310  	}
   311  
   312  	for i := 0; i < vm.Size; i++ {
   313  		Equal(t, vm.Buf[i], vm.Buf[i+vm.Size])
   314  	}
   315  
   316  	vm.Zeroes()
   317  
   318  	for i := range vm.Buf {
   319  		Zero(t, vm.Buf[i])
   320  	}
   321  
   322  	for i := 0; i < vm.Size; i++ {
   323  		vm.Buf[i+vm.Size] = data[i]
   324  	}
   325  
   326  	for i := 0; i < vm.Size; i++ {
   327  		Equal(t, vm.Buf[i], vm.Buf[i+vm.Size])
   328  	}
   329  
   330  	vm.Zeroes()
   331  
   332  	for i := range vm.Buf {
   333  		Zero(t, vm.Buf[i])
   334  	}
   335  }