Skip to content

Commit 28d5bdd

Browse files
authored
Merge pull request #3396 from brancz/ulimit-parse-perf
container/libcontainer: Improve limits file parsing perf
2 parents fbd519b + 4c90b90 commit 28d5bdd

File tree

3 files changed

+78
-35
lines changed

3 files changed

+78
-35
lines changed

container/libcontainer/handler.go

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import (
3939
)
4040

4141
var (
42-
whitelistedUlimits = [...]string{"max_open_files"}
4342
referencedResetInterval = flag.Uint64("referenced_reset_interval", 0,
4443
"Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)")
4544

@@ -205,51 +204,53 @@ func parseUlimit(value string) (int64, error) {
205204
return num, nil
206205
}
207206

208-
func isUlimitWhitelisted(name string) bool {
209-
for _, whitelist := range whitelistedUlimits {
210-
if name == whitelist {
211-
return true
212-
}
213-
}
214-
return false
215-
}
216-
217207
func processLimitsFile(fileData string) []info.UlimitSpec {
208+
const maxOpenFilesLinePrefix = "Max open files"
209+
218210
limits := strings.Split(fileData, "\n")
219211
ulimits := make([]info.UlimitSpec, 0, len(limits))
220212
for _, lim := range limits {
221213
// Skip any headers/footers
222-
if strings.HasPrefix(lim, "Max") {
223-
224-
// Line format: Max open files 16384 16384 files
225-
fields := regexp.MustCompile(`[\s]{2,}`).Split(lim, -1)
226-
name := strings.Replace(strings.ToLower(strings.TrimSpace(fields[0])), " ", "_", -1)
227-
228-
found := isUlimitWhitelisted(name)
229-
if !found {
230-
continue
231-
}
232-
233-
soft := strings.TrimSpace(fields[1])
234-
softNum, softErr := parseUlimit(soft)
235-
236-
hard := strings.TrimSpace(fields[2])
237-
hardNum, hardErr := parseUlimit(hard)
238-
239-
// Omit metric if there were any parsing errors
240-
if softErr == nil && hardErr == nil {
241-
ulimitSpec := info.UlimitSpec{
242-
Name: name,
243-
SoftLimit: int64(softNum),
244-
HardLimit: int64(hardNum),
245-
}
246-
ulimits = append(ulimits, ulimitSpec)
214+
if strings.HasPrefix(lim, "Max open files") {
215+
// Remove line prefix
216+
ulimit, err := processMaxOpenFileLimitLine(
217+
"max_open_files",
218+
lim[len(maxOpenFilesLinePrefix):],
219+
)
220+
if err == nil {
221+
ulimits = append(ulimits, ulimit)
247222
}
248223
}
249224
}
250225
return ulimits
251226
}
252227

228+
// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line.
229+
// with the "Max open files" prefix.
230+
func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) {
231+
// Remove any leading whitespace
232+
line = strings.TrimSpace(line)
233+
// Split on whitespace
234+
fields := strings.Fields(line)
235+
if len(fields) != 3 {
236+
return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line)
237+
}
238+
// The first field is the soft limit, the second is the hard limit
239+
soft, err := parseUlimit(fields[0])
240+
if err != nil {
241+
return info.UlimitSpec{}, err
242+
}
243+
hard, err := parseUlimit(fields[1])
244+
if err != nil {
245+
return info.UlimitSpec{}, err
246+
}
247+
return info.UlimitSpec{
248+
Name: name,
249+
SoftLimit: soft,
250+
HardLimit: hard,
251+
}, nil
252+
}
253+
253254
func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
254255
filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits")
255256
out, err := os.ReadFile(filePath)

container/libcontainer/handler_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,28 @@ func TestClearReferencedBytesWhenClearRefsMissing(t *testing.T) {
296296
err := clearReferencedBytes(pids, 0, 1)
297297
assert.Nil(t, err)
298298
}
299+
300+
var ulimits []info.UlimitSpec
301+
302+
func BenchmarkProcessLimitsFile(b *testing.B) {
303+
content, err := os.ReadFile("testdata/limits")
304+
assert.Nil(b, err)
305+
306+
b.ResetTimer()
307+
for i := 0; i < b.N; i++ {
308+
ulimits = processLimitsFile(string(content))
309+
}
310+
311+
// Ensure the compiler doesn't optimize away the benchmark
312+
_ = ulimits
313+
}
314+
315+
func TestProcessMaxOpenFileLimitLine(t *testing.T) {
316+
line := " 1073741816 1073741816 files "
317+
318+
ulimit, err := processMaxOpenFileLimitLine("max_open_files", line)
319+
assert.Nil(t, err)
320+
assert.Equal(t, "max_open_files", ulimit.Name)
321+
assert.Equal(t, int64(1073741816), ulimit.SoftLimit)
322+
assert.Equal(t, int64(1073741816), ulimit.HardLimit)
323+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Limit Soft Limit Hard Limit Units
2+
Max cpu time unlimited unlimited seconds
3+
Max file size unlimited unlimited bytes
4+
Max data size unlimited unlimited bytes
5+
Max stack size 8388608 unlimited bytes
6+
Max core file size unlimited unlimited bytes
7+
Max resident set unlimited unlimited bytes
8+
Max processes 119958 119958 processes
9+
Max open files 1073741816 1073741816 files
10+
Max locked memory 3932852224 3932852224 bytes
11+
Max address space unlimited unlimited bytes
12+
Max file locks unlimited unlimited locks
13+
Max pending signals 119958 119958 signals
14+
Max msgqueue size 819200 819200 bytes
15+
Max nice priority 0 0
16+
Max realtime priority 0 0
17+
Max realtime timeout unlimited unlimited us

0 commit comments

Comments
 (0)