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 }