@@ -30,6 +30,8 @@ import (
3030 "time"
3131
3232 "github.com/elastic/elastic-agent-libs/logp"
33+
34+ "github.com/elastic/elastic-agent-system-metrics/metric/system/cgroup/stringutil"
3335 "github.com/elastic/elastic-agent-system-metrics/metric/system/resolve"
3436)
3537
@@ -120,35 +122,36 @@ func (pl PathList) Flatten() []ControllerPath {
120122func parseMountinfoLine (line string ) (mountinfo , error ) {
121123 mount := mountinfo {}
122124
123- fields := strings .Fields (line )
124- if len (fields ) < 10 {
125+ var fields [10 + 6 ]string // support up to 6 optional fields
126+
127+ nFields := stringutil .FieldsN (line , fields [:])
128+ if nFields < 10 {
125129 return mount , fmt .Errorf ("invalid mountinfo line, expected at least " +
126- "10 fields but got %d from line='%s'" , len ( fields ) , line )
130+ "10 fields but got %d from line='%s'" , nFields , line )
127131 }
128132
129- mount .mountpoint = fields [4 ]
130-
131133 var separatorIndex int
132- for i , value := range fields {
133- if value == "-" {
134+ for i := 6 ; i < nFields ; i ++ {
135+ if fields [ i ] == "-" {
134136 separatorIndex = i
135137 break
136138 }
137139 }
138- if fields [ separatorIndex ] != "-" {
140+ if separatorIndex == 0 {
139141 return mount , fmt .Errorf ("invalid mountinfo line, separator ('-') not " +
140142 "found in line='%s'" , line )
141143 }
142-
143- if len (fields )- separatorIndex - 1 < 3 {
144+ if nFields - separatorIndex - 1 < 3 {
144145 return mount , fmt .Errorf ("invalid mountinfo line, expected at least " +
145146 "3 fields after separator but got %d from line='%s'" ,
146- len ( fields ) - separatorIndex - 1 , line )
147+ nFields - separatorIndex - 1 , line )
147148 }
148149
149- fields = fields [separatorIndex + 1 :]
150- mount .filesystemType = fields [0 ]
151- mount .superOptions = strings .Split (fields [2 ], "," )
150+ mount .mountpoint = fields [4 ]
151+ mount .filesystemType = fields [separatorIndex + 1 ]
152+ if mount .filesystemType == "cgroup" {
153+ mount .superOptions = stringutil .SplitInline (fields [separatorIndex + 3 ], "," )
154+ }
152155 return mount , nil
153156}
154157
@@ -210,6 +213,7 @@ func SubsystemMountpoints(rootfs resolve.Resolver, subsystems map[string]struct{
210213 }
211214 defer mountinfo .Close ()
212215
216+ hostFS := rootfs .ResolveHostFS ("" )
213217 mounts := map [string ]string {}
214218 mountInfo := Mountpoints {}
215219 sc := bufio .NewScanner (mountinfo )
@@ -218,7 +222,11 @@ func SubsystemMountpoints(rootfs resolve.Resolver, subsystems map[string]struct{
218222 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt
219223 // Example:
220224 // 25 21 0:20 / /cgroup/cpu rw,relatime - cgroup cgroup rw,cpu
221- line := strings .TrimSpace (sc .Text ())
225+
226+ // Avoid heap allocation by not using scanner.Text().
227+ // NOTE: The underlying bytes will change with the next call to scanner.Scan(),
228+ // so make sure to not keep any references after the end of the loop iteration.
229+ line := strings .TrimSpace (stringutil .ByteSlice2String (sc .Bytes ()))
222230 if line == "" {
223231 continue
224232 }
@@ -229,32 +237,32 @@ func SubsystemMountpoints(rootfs resolve.Resolver, subsystems map[string]struct{
229237 }
230238
231239 // if the mountpoint from the subsystem has a different root than ours, it probably belongs to something else.
232- if ! strings .HasPrefix (mount .mountpoint , rootfs . ResolveHostFS ( "" ) ) {
240+ if ! strings .HasPrefix (mount .mountpoint , hostFS ) {
233241 continue
234242 }
235243
236244 // cgroupv1 option
237245 if mount .filesystemType == "cgroup" {
238246 for _ , opt := range mount .superOptions {
239247 // Sometimes the subsystem name is written like "name=blkio".
240- fields := strings . SplitN ( opt , "=" , 2 )
241- if len ( fields ) > 1 {
248+ var fields [ 2 ] string
249+ if n := stringutil . SplitN ( opt , "=" , fields [:]); n > 1 {
242250 opt = fields [1 ]
243251 }
244252
245253 // Test if option is a subsystem name.
246254 if _ , found := subsystems [opt ]; found {
247255 // Add the subsystem mount if it does not already exist.
248256 if _ , exists := mounts [opt ]; ! exists {
249- mounts [opt ] = mount .mountpoint
257+ mounts [opt ] = strings . Clone ( mount .mountpoint )
250258 }
251259 }
252260 }
253261 }
254262
255263 // V2 option
256264 if mount .filesystemType == "cgroup2" {
257- possibleV2Paths = append (possibleV2Paths , mount .mountpoint )
265+ possibleV2Paths = append (possibleV2Paths , strings . Clone ( mount .mountpoint ) )
258266 }
259267
260268 }
@@ -290,9 +298,25 @@ func isCgroupNSPrivate() bool {
290298 }
291299 // if we have a path of just "/" that means we're in our own private namespace
292300 // if it's something else, we're probably in a host namespace
293- segments := strings .Split (strings .TrimSpace (string (raw )), ":" )
294- return segments [len (segments )- 1 ] == "/"
301+ colons := 2
302+ for i := range trimBytes (raw ) {
303+ if raw [i ] == ':' {
304+ colons ++
305+ if colons == 2 {
306+ // if we have a second colon, check if the next character is a slash
307+ return len (raw ) == i + 2 && raw [i + 1 ] == '/'
308+ }
309+ }
310+ }
311+ return false
312+ }
295313
314+ func trimBytes (b []byte ) []byte {
315+ // trim the trailing newlines
316+ for i := len (b ) - 1 ; i >= 0 && b [i ] == '\n' ; i -- {
317+ b = b [:i ]
318+ }
319+ return b
296320}
297321
298322// tries to find the cgroup path for the currently-running container,
0 commit comments