github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/ext/benchmark/benchmark_test.go (about)

     1  // Copyright 2019 The gVisor Authors.
     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  // These benchmarks emulate memfs benchmarks. Ext4 images must be created
    16  // before this benchmark is run using the `make_deep_ext4.sh` script at
    17  // /tmp/image-{depth}.ext4 for all the depths tested below.
    18  //
    19  // The benchmark itself cannot run the script because the script requires
    20  // sudo privileges to create the file system images.
    21  package benchmark_test
    22  
    23  import (
    24  	"fmt"
    25  	"os"
    26  	"runtime"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/SagerNet/gvisor/pkg/context"
    31  	"github.com/SagerNet/gvisor/pkg/fspath"
    32  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    33  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/ext"
    34  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    35  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    36  )
    37  
    38  var depths = []int{1, 2, 3, 8, 64, 100}
    39  
    40  const filename = "file.txt"
    41  
    42  // setUp opens imagePath as an ext Filesystem and returns all necessary
    43  // elements required to run tests. If error is nil, it also returns a tear
    44  // down function which must be called after the test is run for clean up.
    45  func setUp(b *testing.B, imagePath string) (context.Context, *vfs.VirtualFilesystem, *vfs.VirtualDentry, func(), error) {
    46  	f, err := os.Open(imagePath)
    47  	if err != nil {
    48  		return nil, nil, nil, nil, err
    49  	}
    50  
    51  	ctx := contexttest.Context(b)
    52  	creds := auth.CredentialsFromContext(ctx)
    53  
    54  	// Create VFS.
    55  	vfsObj := &vfs.VirtualFilesystem{}
    56  	if err := vfsObj.Init(ctx); err != nil {
    57  		return nil, nil, nil, nil, err
    58  	}
    59  	vfsObj.MustRegisterFilesystemType("extfs", ext.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
    60  		AllowUserMount: true,
    61  	})
    62  	mntns, err := vfsObj.NewMountNamespace(ctx, creds, imagePath, "extfs", &vfs.MountOptions{
    63  		GetFilesystemOptions: vfs.GetFilesystemOptions{
    64  			InternalData: int(f.Fd()),
    65  		},
    66  	})
    67  	if err != nil {
    68  		f.Close()
    69  		return nil, nil, nil, nil, err
    70  	}
    71  
    72  	root := mntns.Root()
    73  	root.IncRef()
    74  
    75  	tearDown := func() {
    76  		root.DecRef(ctx)
    77  
    78  		if err := f.Close(); err != nil {
    79  			b.Fatalf("tearDown failed: %v", err)
    80  		}
    81  	}
    82  	return ctx, vfsObj, &root, tearDown, nil
    83  }
    84  
    85  // mount mounts extfs at the path operation passed. Returns a tear down
    86  // function which must be called after the test is run for clean up.
    87  func mount(b *testing.B, imagePath string, vfsfs *vfs.VirtualFilesystem, pop *vfs.PathOperation) func() {
    88  	b.Helper()
    89  
    90  	f, err := os.Open(imagePath)
    91  	if err != nil {
    92  		b.Fatalf("could not open image at %s: %v", imagePath, err)
    93  	}
    94  
    95  	ctx := contexttest.Context(b)
    96  	creds := auth.CredentialsFromContext(ctx)
    97  
    98  	if _, err := vfsfs.MountAt(ctx, creds, imagePath, pop, "extfs", &vfs.MountOptions{
    99  		GetFilesystemOptions: vfs.GetFilesystemOptions{
   100  			InternalData: int(f.Fd()),
   101  		},
   102  	}); err != nil {
   103  		b.Fatalf("failed to mount tmpfs submount: %v", err)
   104  	}
   105  	return func() {
   106  		if err := f.Close(); err != nil {
   107  			b.Fatalf("tearDown failed: %v", err)
   108  		}
   109  	}
   110  }
   111  
   112  // BenchmarkVFS2Ext4fsStat emulates BenchmarkVFS2MemfsStat.
   113  func BenchmarkVFS2Ext4fsStat(b *testing.B) {
   114  	for _, depth := range depths {
   115  		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
   116  			ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", depth))
   117  			if err != nil {
   118  				b.Fatalf("setUp failed: %v", err)
   119  			}
   120  			defer tearDown()
   121  
   122  			creds := auth.CredentialsFromContext(ctx)
   123  			var filePathBuilder strings.Builder
   124  			filePathBuilder.WriteByte('/')
   125  			for i := 1; i <= depth; i++ {
   126  				filePathBuilder.WriteString(fmt.Sprintf("%d", i))
   127  				filePathBuilder.WriteByte('/')
   128  			}
   129  			filePathBuilder.WriteString(filename)
   130  			filePath := filePathBuilder.String()
   131  
   132  			runtime.GC()
   133  			b.ResetTimer()
   134  			for i := 0; i < b.N; i++ {
   135  				stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
   136  					Root:               *root,
   137  					Start:              *root,
   138  					Path:               fspath.Parse(filePath),
   139  					FollowFinalSymlink: true,
   140  				}, &vfs.StatOptions{})
   141  				if err != nil {
   142  					b.Fatalf("stat(%q) failed: %v", filePath, err)
   143  				}
   144  				// Sanity check.
   145  				if stat.Size > 0 {
   146  					b.Fatalf("got wrong file size (%d)", stat.Size)
   147  				}
   148  			}
   149  		})
   150  	}
   151  }
   152  
   153  // BenchmarkVFS2ExtfsMountStat emulates BenchmarkVFS2MemfsMountStat.
   154  func BenchmarkVFS2ExtfsMountStat(b *testing.B) {
   155  	for _, depth := range depths {
   156  		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
   157  			// Create root extfs with depth 1 so we can mount extfs again at /1/.
   158  			ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", 1))
   159  			if err != nil {
   160  				b.Fatalf("setUp failed: %v", err)
   161  			}
   162  			defer tearDown()
   163  
   164  			creds := auth.CredentialsFromContext(ctx)
   165  			mountPointName := "/1/"
   166  			pop := vfs.PathOperation{
   167  				Root:  *root,
   168  				Start: *root,
   169  				Path:  fspath.Parse(mountPointName),
   170  			}
   171  
   172  			// Save the mount point for later use.
   173  			mountPoint, err := vfsfs.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
   174  			if err != nil {
   175  				b.Fatalf("failed to walk to mount point: %v", err)
   176  			}
   177  			defer mountPoint.DecRef(ctx)
   178  
   179  			// Create extfs submount.
   180  			mountTearDown := mount(b, fmt.Sprintf("/tmp/image-%d.ext4", depth), vfsfs, &pop)
   181  			defer mountTearDown()
   182  
   183  			var filePathBuilder strings.Builder
   184  			filePathBuilder.WriteString(mountPointName)
   185  			for i := 1; i <= depth; i++ {
   186  				filePathBuilder.WriteString(fmt.Sprintf("%d", i))
   187  				filePathBuilder.WriteByte('/')
   188  			}
   189  			filePathBuilder.WriteString(filename)
   190  			filePath := filePathBuilder.String()
   191  
   192  			runtime.GC()
   193  			b.ResetTimer()
   194  			for i := 0; i < b.N; i++ {
   195  				stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
   196  					Root:               *root,
   197  					Start:              *root,
   198  					Path:               fspath.Parse(filePath),
   199  					FollowFinalSymlink: true,
   200  				}, &vfs.StatOptions{})
   201  				if err != nil {
   202  					b.Fatalf("stat(%q) failed: %v", filePath, err)
   203  				}
   204  				// Sanity check. touch(1) always creates files of size 0 (empty).
   205  				if stat.Size > 0 {
   206  					b.Fatalf("got wrong file size (%d)", stat.Size)
   207  				}
   208  			}
   209  		})
   210  	}
   211  }