Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Procfs improvements #4540

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Procfs improvements #4540

wants to merge 8 commits into from

Conversation

geyslan
Copy link
Member

@geyslan geyslan commented Jan 21, 2025

Close: #4547

1. Explain what the PR does

9a53e02 perf(proctree): remove stat call
31937e6 chore: introduce builders with specific fields
3d5afb5 perf(proc): improve ns
58bc552 perf(proc): improve status file parsing
4b64197 perf(proctree/proc): align fields to real size
bfe8fc8 perf(proc): improve stat file parsing
c5f8cb0 perf(proc): introduce ReadFile for /proc
1b6899d perf(proc): use formatters for procfs file paths

9a53e02 perf(proctree): remove stat call

Calling stat on /proc/<pid> would only increase the window for
process termination between the stat call and the read of the file.

This also replaces fmt.Sprintf with string concatenation and
strconv.FormatInt for better performance.

31937e6 chore: introduce builders with specific fields

- NewProcStatFields()
- NewThreadStatFields()
- NewProcStatusFields()
- NewThreadStatusFields()

3d5afb5 perf(proc): improve ns

Reduce ProcNS memory footprint by using the right member type sizes -
namespace id is an uint32, since it is the inode number in
struct ns_common.

This change also improves the performance of GetAllProcNS(), GetProcNS()
and GetMountNSFirstProcesses().

58bc552 perf(proc): improve status file parsing

Remove the use of library functions to parse the status file and instead
parse it manually (on the fly) to reduce the number of allocations and
improve performance.

4b64197 perf(proctree/proc): align fields to real size

Propagate values based on its real size which in most cases is smaller
than int (64-bit). This change reduces the memory footprint or at least
the stress on the stack/heap.

bfe8fc8 perf(proc): improve stat file parsing

Remove the use of library functions to parse the stat file and instead
parse it manually (on the fly) to reduce the number of allocations and
improve performance.

chore(proc): align parsing of stat field with the formats size

This also align parsing sizes with the formats to avoid wrong parsing
of the stat file. The internal fields are represented aligned with the
actual kernel fields to avoid any confusion (signed/unsigned).

c5f8cb0 perf(proc): introduce ReadFile for /proc

`os.ReadFile` is not efficient for reading files in `/proc` because it
attempts to determine the file size before reading. This step is
unnecessary for `/proc` files, as they are virtual files with sizes
that are often reported as unknown or `0`.

`proc.ReadFile` is a new function designed specifically for reading
files in `/proc`. It reads directly into a buffer and is more efficient
than `os.ReadFile` because it allows tuning the initial buffer size to
better suit the characteristics of `/proc` files.

Running tool: /home/gg/.goenv/versions/1.22.4/bin/go test -benchmem
-run=^$ -tags ebpf -bench ^BenchmarkReadFile$
github.com/aquasecurity/tracee/pkg/utils/proc -benchtime=10000000x

goos: linux
goarch: amd64
pkg: github.com/aquasecurity/tracee/pkg/utils/proc
cpu: AMD Ryzen 9 7950X 16-Core Processor
BenchmarkReadFile/ProcFSReadFile/Empty_File-32        10000000  3525 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile/Empty_File-32            10000000  4070 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile/Small_File-32        10000000  3961 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile/Small_File-32            10000000  4538 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile/Exact_Buffer_Size-32 10000000  4229 ns/op  920 B/op  5 allocs/op
BenchmarkReadFile/OsReadFile/Exact_Buffer_Size-32     10000000  4523 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile_/proc/self/stat-32   10000000  4043 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile_/proc/self/stat-32       10000000  4585 ns/op  872 B/op  5 allocs/op
PASS
ok      github.com/aquasecurity/tracee/pkg/utils/proc   334.751s

1b6899d perf(proc): use formatters for procfs file paths

Since the type of the converted primitive is already known, formatter
helpers should be used to construct procfs file paths instead of relying
on `fmt.Sprintf`. Using `fmt.Sprintf` is relatively costly due to its
internal formatting logic, which is unnecessary for simple path
construction.

2. Explain how to test it

3. Other comments

@geyslan geyslan self-assigned this Jan 21, 2025
pkg/utils/proc/ns.go Fixed Show fixed Hide fixed
pkg/utils/proc/ns.go Fixed Show fixed Hide fixed
Since the type of the converted primitive is already known, formatter
helpers should be used to construct procfs file paths instead of relying
on `fmt.Sprintf`. Using `fmt.Sprintf` is relatively costly due to its
internal formatting logic, which is unnecessary for simple path
construction.
`os.ReadFile` is not efficient for reading files in `/proc` because it
attempts to determine the file size before reading. This step is
unnecessary for `/proc` files, as they are virtual files with sizes
that are often reported as unknown or `0`.

`proc.ReadFile` is a new function designed specifically for reading
files in `/proc`. It reads directly into a buffer and is more efficient
than `os.ReadFile` because it allows tuning the initial buffer size to
better suit the characteristics of `/proc` files.

Running tool: /home/gg/.goenv/versions/1.22.4/bin/go test -benchmem
-run=^$ -tags ebpf -bench ^BenchmarkReadFile$
github.com/aquasecurity/tracee/pkg/utils/proc -benchtime=10000000x

goos: linux
goarch: amd64
pkg: github.com/aquasecurity/tracee/pkg/utils/proc
cpu: AMD Ryzen 9 7950X 16-Core Processor
BenchmarkReadFile/ProcFSReadFile/Empty_File-32        10000000  3525 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile/Empty_File-32            10000000  4070 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile/Small_File-32        10000000  3961 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile/Small_File-32            10000000  4538 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile/Exact_Buffer_Size-32 10000000  4229 ns/op  920 B/op  5 allocs/op
BenchmarkReadFile/OsReadFile/Exact_Buffer_Size-32     10000000  4523 ns/op  872 B/op  5 allocs/op
BenchmarkReadFile/ProcFSReadFile_/proc/self/stat-32   10000000  4043 ns/op  408 B/op  4 allocs/op
BenchmarkReadFile/OsReadFile_/proc/self/stat-32       10000000  4585 ns/op  872 B/op  5 allocs/op
PASS
ok  	github.com/aquasecurity/tracee/pkg/utils/proc	334.751s
Remove the use of library functions to parse the stat file and instead
parse it manually (on the fly) to reduce the number of allocations and
improve performance.

chore(proc): align parsing of stat field with the formats size

This also align parsing sizes with the formats to avoid wrong parsing
of the stat file. The internal fields are represented aligned with the
actual kernel fields to avoid any confusion (signed/unsigned).
Propagate values based on its real size which in most cases is smaller
than int (64-bit). This change reduces the memory footprint or at least
the stress on the stack/heap.
Remove the use of library functions to parse the status file and instead
parse it manually (on the fly) to reduce the number of allocations and
improve performance.
Reduce ProcNS memory footprint by using the right member type sizes -
namespace id is an uint32, since it is the inode number in
struct ns_common.

This change also improves the performance of GetAllProcNS(), GetProcNS()
and GetMountNSFirstProcesses().
- NewProcStatFields()
- NewThreadStatFields()
- NewProcStatusFields()
- NewThreadStatusFields()
Calling stat on /proc/<pid> would only increase the window for
process termination between the stat call and the read of the file.

This also replaces fmt.Sprintf with string concatenation and
strconv.FormatInt for better performance.
@geyslan geyslan marked this pull request as ready for review February 4, 2025 01:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

reduce cpu/memory of proc pkg
1 participant