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

Add /proc/modules support #476

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions proc_modules.go
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) {
Dentrax marked this conversation as resolved.
Show resolved Hide resolved
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 {
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it expected that the parsing might fail?

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.)

Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not return an error if err != nil?

That's because I noticed this err == nil pattern in some places. For example, arp:

procfs/arp.go

Line 98 in 0c4b3aa

if mac, err := net.ParseMAC(columns[3]); err == nil {
zoneinfo:
if err == nil {

I agree with you, returning an error would better. I can return an error if you want. Wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SuperQ wdyt? But yeah I think returning an error would be better

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, returning the error would be better.

Copy link
Member

Choose a reason for hiding this comment

The 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()
}
97 changes: 97 additions & 0 deletions proc_modules_test.go
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)
}
})
}
}
93 changes: 93 additions & 0 deletions testdata/fixtures.ttar
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,99 @@ DirectMap4k: 91136 kB
DirectMap2M: 16039936 kB
Mode: 664
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/proc/modules
Lines: 89
nft_counter 16384 4 - Live 0xffffffffc0c77000
nft_chain_nat 16384 2 - Live 0xffffffffc0c72000
nft_compat 20480 8 - Live 0xffffffffc0c6c000
xt_tcpudp 20480 4 - Live 0xffffffffc0c66000
xt_nat 16384 4 - Live 0xffffffffc0c61000
xt_multiport 20480 0 - Live 0xffffffffc0c5b000
xt_mark 16384 0 - Live 0xffffffffc0c56000
xt_conntrack 16384 0 - Live 0xffffffffc0c42000
xt_comment 16384 0 - Live 0xffffffffc0c3d000
xt_addrtype 16384 0 - Live 0xffffffffc0c51000
xt_MASQUERADE 20480 0 - Live 0xffffffffc0c4b000
nf_tables 245760 19 nft_counter,nft_chain_nat,nft_compat, Live 0xffffffffc0c00000
nfnetlink 20480 2 nft_compat,nf_tables, Live 0xffffffffc0bfa000
ip6table_filter 16384 0 - Live 0xffffffffc0b8b000
iptable_filter 16384 0 - Live 0xffffffffc0bde000
ip6table_nat 16384 0 - Live 0xffffffffc0bd9000
iptable_nat 16384 0 - Live 0xffffffffc0bf5000
nf_nat 49152 5 nft_chain_nat,xt_nat,xt_MASQUERADE,ip6table_nat,iptable_nat, Live 0xffffffffc0be8000
nf_conntrack 172032 4 xt_nat,xt_conntrack,xt_MASQUERADE,nf_nat, Live 0xffffffffc0bae000
nf_defrag_ipv6 24576 1 nf_conntrack, Live 0xffffffffc0ba3000
nf_defrag_ipv4 16384 1 nf_conntrack, Live 0xffffffffc0b9e000
ip6_tables 32768 2 ip6table_filter,ip6table_nat, Live 0xffffffffc0b82000
veth 32768 0 - Live 0xffffffffc0b95000
bridge 307200 0 - Live 0xffffffffc0b36000
stp 16384 1 bridge, Live 0xffffffffc0b2f000
llc 16384 2 bridge,stp, Live 0xffffffffc0b26000
tap 28672 0 - Live 0xffffffffc0b1b000
binfmt_misc 24576 1 - Live 0xffffffffc0ad6000
overlay 151552 0 - Live 0xffffffffc0ab0000
isofs 53248 1 - Live 0xffffffffc09d0000
nls_iso8859_1 16384 1 - Live 0xffffffffc097f000
input_leds 16384 0 - Live 0xffffffffc099d000
serio_raw 20480 0 - Live 0xffffffffc098b000
virtio_input 20480 0 - Live 0xffffffffc0979000
dm_multipath 40960 0 - Live 0xffffffffc093f000
sch_fq_codel 20480 3 - Live 0xffffffffc0939000
scsi_dh_rdac 20480 0 - Live 0xffffffffc0930000
scsi_dh_emc 16384 0 - Live 0xffffffffc0928000
scsi_dh_alua 20480 0 - Live 0xffffffffc0917000
ipmi_devintf 20480 0 - Live 0xffffffffc091e000
ipmi_msghandler 122880 1 ipmi_devintf, Live 0xffffffffc08f8000
msr 16384 0 - Live 0xffffffffc08f0000
efi_pstore 16384 0 - Live 0xffffffffc08eb000
virtio_rng 16384 0 - Live 0xffffffffc0581000
ip_tables 32768 2 iptable_filter,iptable_nat, Live 0xffffffffc0743000
x_tables 53248 15 nft_compat,xt_tcpudp,xt_nat,xt_multiport,xt_mark,xt_conntrack,xt_comment,xt_addrtype,xt_MASQUERADE,ip6table_filter,iptable_filter,ip6table_nat,iptable_nat,ip6_tables,ip_tables, Live 0xffffffffc0735000
autofs4 49152 2 - Live 0xffffffffc0728000
btrfs 1552384 0 - Live 0xffffffffc0761000
blake2b_generic 20480 0 - Live 0xffffffffc06d3000
zstd_compress 229376 1 btrfs, Live 0xffffffffc06ef000
raid10 69632 0 - Live 0xffffffffc06dd000
raid456 163840 0 - Live 0xffffffffc06aa000
async_raid6_recov 24576 1 raid456, Live 0xffffffffc06a3000
async_memcpy 20480 2 raid456,async_raid6_recov, Live 0xffffffffc069d000
async_pq 24576 2 raid456,async_raid6_recov, Live 0xffffffffc0696000
async_xor 20480 3 raid456,async_raid6_recov,async_pq, Live 0xffffffffc0690000
async_tx 20480 5 raid456,async_raid6_recov,async_memcpy,async_pq,async_xor, Live 0xffffffffc068a000
xor 24576 2 btrfs,async_xor, Live 0xffffffffc0593000
raid6_pq 122880 4 btrfs,raid456,async_raid6_recov,async_pq, Live 0xffffffffc066b000
libcrc32c 16384 5 nf_tables,nf_nat,nf_conntrack,btrfs,raid456, Live 0xffffffffc0556000
raid1 49152 0 - Live 0xffffffffc0610000
raid0 24576 0 - Live 0xffffffffc0517000
multipath 20480 0 - Live 0xffffffffc0501000
linear 20480 0 - Live 0xffffffffc045b000
virtio_gpu 69632 0 - Live 0xffffffffc05a0000
virtio_dma_buf 16384 1 virtio_gpu, Live 0xffffffffc0536000
crct10dif_pclmul 16384 1 - Live 0xffffffffc050e000
drm_kms_helper 311296 3 virtio_gpu, Live 0xffffffffc061e000
crc32_pclmul 16384 0 - Live 0xffffffffc0527000
syscopyarea 16384 1 drm_kms_helper, Live 0xffffffffc0520000
sysfillrect 20480 1 drm_kms_helper, Live 0xffffffffc0508000
ghash_clmulni_intel 16384 0 - Live 0xffffffffc0456000
sysimgblt 16384 1 drm_kms_helper, Live 0xffffffffc04fc000
fb_sys_fops 16384 1 drm_kms_helper, Live 0xffffffffc0425000
cec 61440 1 drm_kms_helper, Live 0xffffffffc0751000
rc_core 65536 1 cec, Live 0xffffffffc0540000
aesni_intel 376832 0 - Live 0xffffffffc05b3000
crypto_simd 16384 1 aesni_intel, Live 0xffffffffc059b000
ahci 45056 1 - Live 0xffffffffc0587000
virtio_net 61440 0 - Live 0xffffffffc0571000
xhci_pci 24576 0 - Live 0xffffffffc0562000
net_failover 20480 1 virtio_net, Live 0xffffffffc055c000
cryptd 24576 2 ghash_clmulni_intel,crypto_simd, Live 0xffffffffc052c000
drm 622592 3 virtio_gpu,drm_kms_helper, Live 0xffffffffc0463000 (POE)
psmouse 176128 0 - Live 0xffffffffc042a000
libahci 45056 1 ahci, Live 0xffffffffc0419000
virtio_blk 20480 2 - Live 0xffffffffc040f000
xhci_pci_renesas 20480 1 xhci_pci, Live 0xffffffffc0407000
failover 16384 1 net_failover, Live 0xffffffffc03ff000 (P)
Mode: 644
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Directory: fixtures/proc/net
Mode: 755
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down