github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/process/process_linux_test.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package process 22 23 import ( 24 "fmt" 25 "io/ioutil" 26 "math" 27 "os" 28 "path/filepath" 29 "testing" 30 31 "github.com/stretchr/testify/require" 32 ) 33 34 type cleanupFn func() 35 36 // Stdin 37 // stdout 38 // stderr 39 // /proc/<PID>/fd 40 // One more (not sure what it is, probably something related to the Go test runner.) 41 const numStdProcessFiles = 5 42 43 // Sometimes the number of F.Ds is higher than expected (likely due to the test runner 44 // or other other tests that didn't clean up FDs properly.) 45 const allowedMarginOfError = 2 46 47 func TestNumFDs(t *testing.T) { 48 for i := 0; i <= 8; i++ { 49 var numFiles int 50 if i == 0 { 51 numFiles = 0 52 } else { 53 numFiles = int(math.Pow(float64(2), float64(i))) 54 } 55 56 func() { 57 numExpectedFds := numFiles + numStdProcessFiles 58 cleanupFn := createTempFiles(numFiles) 59 defer cleanupFn() 60 61 selfPID := os.Getpid() 62 63 t.Run(fmt.Sprintf("func: %s, numFiles: %d", "numFDsSlow", numFiles), func(t *testing.T) { 64 numFDs, err := numFDsSlow(selfPID) 65 require.NoError(t, err) 66 verifyNumFDsWithinMarginOfError(t, numExpectedFds, numFDs) 67 }) 68 69 t.Run(fmt.Sprintf("func: %s, numFiles: %d", "NumFDs", numFiles), func(t *testing.T) { 70 numFDs, err := NumFDs(selfPID) 71 require.NoError(t, err) 72 verifyNumFDsWithinMarginOfError(t, numExpectedFds, numFDs) 73 }) 74 75 t.Run(fmt.Sprintf("func: %s, numFiles: %d", "NumFDsWithDefaultBatchSleep", numFiles), func(t *testing.T) { 76 numFDs, err := NumFDsWithDefaultBatchSleep(selfPID) 77 require.NoError(t, err) 78 verifyNumFDsWithinMarginOfError(t, numExpectedFds, numFDs) 79 }) 80 }() 81 } 82 } 83 84 func verifyNumFDsWithinMarginOfError(t *testing.T, expected, actual int) { 85 require.True( 86 t, 87 actual-expected <= allowedMarginOfError, 88 fmt.Sprintf("expected: %d, actual: %d, allowed margin of error: %d", 89 expected, actual, allowedMarginOfError), 90 ) 91 } 92 93 func BenchmarkNumFDs(b *testing.B) { 94 var ( 95 // Low for C.I and local testing, bump this up to a much larger number 96 // when performing actual benchmarking. 97 numFiles = 16000 98 // +5 to account for standard F.Ds that each process gets. 99 numExpectedFds = numFiles + numStdProcessFiles 100 ) 101 cleanupFn := createTempFiles(numFiles) 102 defer cleanupFn() 103 104 selfPID := os.Getpid() 105 b.Run("numFDsSlow", func(b *testing.B) { 106 for i := 0; i < b.N; i++ { 107 numFDs, err := numFDsSlow(selfPID) 108 if err != nil { 109 b.Fatal(err) 110 } 111 if numFDs != numExpectedFds { 112 b.Fatalf("expected %d files but got %d", numExpectedFds, numFDs) 113 } 114 } 115 }) 116 117 b.Run("NumFDs", func(b *testing.B) { 118 for i := 0; i < b.N; i++ { 119 numFDs, err := NumFDs(selfPID) 120 if err != nil { 121 b.Fatal(err) 122 } 123 if numFDs != numExpectedFds { 124 b.Fatalf("expected %d files but got %d", numExpectedFds, numFDs) 125 } 126 } 127 }) 128 129 b.Run("NumFDsWithDefaultBatchSleep", func(b *testing.B) { 130 for i := 0; i < b.N; i++ { 131 numFDs, err := NumFDsWithDefaultBatchSleep(selfPID) 132 if err != nil { 133 b.Fatal(err) 134 } 135 if numFDs != numExpectedFds { 136 b.Fatalf("expected %d files but got %d", numExpectedFds, numFDs) 137 } 138 } 139 }) 140 } 141 142 func createTempFiles(numFiles int) cleanupFn { 143 tempDir, err := ioutil.TempDir("", "test") 144 if err != nil { 145 panic(err) 146 } 147 148 files := make([]*os.File, 0, numFiles) 149 for i := 0; i < numFiles; i++ { 150 tempFilePath := filepath.Join(tempDir, fmt.Sprintf("%d.txt", i)) 151 f, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 152 if err != nil { 153 panic(err) 154 } 155 files = append(files, f) 156 } 157 158 return func() { 159 for _, f := range files { 160 f.Close() 161 } 162 os.RemoveAll(tempDir) 163 } 164 165 }