-
Notifications
You must be signed in to change notification settings - Fork 345
Add /proc/modules
support
#476
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,109 @@ | ||||||
// Copyright 2022 The Prometheus Authors | ||||||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
// you may not use this file except in compliance with the License. | ||||||
// You may obtain a copy of the License at | ||||||
// | ||||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||||
// | ||||||
// Unless required by applicable law or agreed to in writing, software | ||||||
// distributed under the License is distributed on an "AS IS" BASIS, | ||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
// See the License for the specific language governing permissions and | ||||||
// limitations under the License. | ||||||
|
||||||
package procfs | ||||||
|
||||||
import ( | ||||||
"bufio" | ||||||
"bytes" | ||||||
"fmt" | ||||||
"io" | ||||||
"strconv" | ||||||
"strings" | ||||||
|
||||||
"github.com/prometheus/procfs/internal/util" | ||||||
) | ||||||
|
||||||
// Module represents a single module info in the kernel. | ||||||
type Module struct { | ||||||
// Name is the name of the module. | ||||||
Name string | ||||||
// Size is the memory size of the module, in bytes | ||||||
Size uint64 | ||||||
// Instances is the number of instances of the module are currently loaded. | ||||||
// A value of zero represents an unloaded module. | ||||||
Instances uint64 | ||||||
// Dependencies is the list of modules that this module depends on. | ||||||
Dependencies []string | ||||||
// State is the state of the module is in: Live, Loading, or Unloading are the only possible values. | ||||||
State string | ||||||
// Offset is a memory offset for the loaded module | ||||||
Offset uint64 | ||||||
// Taints is a list of taints that the module has. | ||||||
Taints []string | ||||||
} | ||||||
|
||||||
// Modules represents a list of Module structs. | ||||||
type Modules []Module | ||||||
|
||||||
// Modules parses the metrics from /proc/modules file and returns a slice of | ||||||
// structs containing the relevant info. | ||||||
func (fs FS) Modules() ([]Module, error) { | ||||||
data, err := util.ReadFileNoStat(fs.proc.Path("modules")) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
return parseModules(bytes.NewReader(data)) | ||||||
} | ||||||
|
||||||
// parseModules parses the metrics from /proc/modules file | ||||||
// and returns a []Module structure. | ||||||
// - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/4/html/reference_guide/s2-proc-modules | ||||||
// - https://unix.stackexchange.com/a/152527/300614 | ||||||
func parseModules(r io.Reader) ([]Module, error) { | ||||||
var ( | ||||||
scanner = bufio.NewScanner(r) | ||||||
modules = make([]Module, 0) | ||||||
) | ||||||
|
||||||
for scanner.Scan() { | ||||||
parts := strings.Fields(scanner.Text()) | ||||||
Dentrax marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
if len(parts) < 6 { | ||||||
return nil, fmt.Errorf("not enough fields in modules (expected at least 6 fields but got %d): %s", len(parts), parts) | ||||||
} | ||||||
|
||||||
module := Module{ | ||||||
Name: parts[0], | ||||||
Dependencies: []string{}, | ||||||
State: parts[4], | ||||||
Taints: []string{}, | ||||||
} | ||||||
|
||||||
if size, err := strconv.ParseUint(parts[1], 10, 64); err == nil { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it expected that the parsing might fail? If not, we should just abort here and return the error then silentely ignoring it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, I think it's possible. Checking the error could also prevent from spec changes. (too low possibility, but still would be better than not checking instead.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah but I don't like that we silentely ignore the error here. Why not return an error if err != nil? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's because I noticed this Line 98 in 0c4b3aa
Line 186 in 0c4b3aa
I agree with you, returning an error would better. I can return an error if you want. Wdyt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @SuperQ wdyt? But yeah I think returning an error would be better There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, returning the error would be better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Dentrax Can you change this so we can merge it? |
||||||
module.Size = size | ||||||
} | ||||||
|
||||||
if instances, err := strconv.ParseUint(parts[2], 10, 64); err == nil { | ||||||
module.Instances = instances | ||||||
} | ||||||
|
||||||
dependencies := parts[3] | ||||||
if dependencies != "-" { | ||||||
module.Dependencies = strings.Split(strings.TrimSuffix(dependencies, ","), ",") | ||||||
} | ||||||
|
||||||
if offset, err := strconv.ParseUint(parts[5], 10, 64); err == nil { | ||||||
module.Offset = offset | ||||||
} | ||||||
|
||||||
// Kernel Taint State is available if parts length is greater than 6. | ||||||
if len(parts) > 6 { | ||||||
taints := strings.TrimSuffix(strings.TrimPrefix(parts[6], "("), ")") | ||||||
module.Taints = strings.Split(taints, "") | ||||||
} | ||||||
|
||||||
modules = append(modules, module) | ||||||
} | ||||||
|
||||||
return modules, scanner.Err() | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright 2022 The Prometheus Authors | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package procfs | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestProcModules(t *testing.T) { | ||
fs := getProcFixtures(t) | ||
|
||
modules, err := fs.Modules() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if want, have := 89, len(modules); want != have { | ||
t.Errorf("want length %d, have %d", want, have) | ||
} | ||
|
||
for _, test := range []struct { | ||
name string | ||
index int | ||
want Module | ||
}{ | ||
{ | ||
name: "no dependencies and taints", | ||
index: 0, | ||
want: Module{ | ||
Name: "nft_counter", | ||
Size: 16384, | ||
Instances: 4, | ||
Dependencies: []string{}, | ||
State: "Live", | ||
Offset: 0, | ||
Taints: []string{}, | ||
}, | ||
}, | ||
{ | ||
name: "have dependencies with no taints", | ||
index: 11, | ||
want: Module{ | ||
Name: "nf_tables", | ||
Size: 245760, | ||
Instances: 19, | ||
Dependencies: []string{"nft_counter", "nft_chain_nat", "nft_compat"}, | ||
State: "Live", | ||
Offset: 0, | ||
Taints: []string{}, | ||
}, | ||
}, | ||
{ | ||
name: "have multiple taints with multiple dependencies", | ||
index: 83, | ||
want: Module{ | ||
Name: "drm", | ||
Size: 622592, | ||
Instances: 3, | ||
Dependencies: []string{"virtio_gpu", "drm_kms_helper"}, | ||
State: "Live", | ||
Offset: 0, | ||
Taints: []string{"P", "O", "E"}, | ||
}, | ||
}, | ||
{ | ||
name: "have single taint with single dependency", | ||
index: 88, | ||
want: Module{ | ||
Name: "failover", | ||
Size: 16384, | ||
Instances: 1, | ||
Dependencies: []string{"net_failover"}, | ||
State: "Live", | ||
Offset: 0, | ||
Taints: []string{"P"}, | ||
}, | ||
}, | ||
} { | ||
t.Run(test.name, func(t *testing.T) { | ||
if want, have := test.want, modules[test.index]; !reflect.DeepEqual(want, have) { | ||
t.Errorf("want %v, have %v", want, have) | ||
} | ||
}) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.