├── .circleci └── config.yml ├── .github ├── dependabot.yml └── workflows │ └── golangci-lint.yml ├── .gitignore ├── .golangci.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── Makefile.common ├── NOTICE ├── README.md ├── SECURITY.md ├── arp.go ├── arp_test.go ├── bcache ├── bcache.go ├── get.go ├── get_test.go └── testdata │ └── fixtures ├── blockdevice ├── stats.go ├── stats_test.go └── testdata │ └── fixtures ├── btrfs ├── btrfs.go ├── get.go ├── get_test.go └── testdata │ └── fixtures ├── buddyinfo.go ├── buddyinfo_test.go ├── cmdline.go ├── cmdline_test.go ├── cpuinfo.go ├── cpuinfo_armx.go ├── cpuinfo_loong64.go ├── cpuinfo_mipsx.go ├── cpuinfo_others.go ├── cpuinfo_ppcx.go ├── cpuinfo_riscvx.go ├── cpuinfo_s390x.go ├── cpuinfo_test.go ├── cpuinfo_x86.go ├── crypto.go ├── crypto_test.go ├── doc.go ├── ext4 └── ext4.go ├── fs.go ├── fs_statfs_notype.go ├── fs_statfs_type.go ├── fs_test.go ├── fscache.go ├── fscache_test.go ├── go.mod ├── go.sum ├── internal ├── fs │ ├── fs.go │ ├── fs_test.go │ └── testdata │ │ └── fixtures └── util │ ├── parse.go │ ├── readfile.go │ ├── sysreadfile.go │ ├── sysreadfile_compat.go │ ├── valueparser.go │ └── valueparser_test.go ├── ipvs.go ├── ipvs_test.go ├── iscsi ├── get.go ├── get_test.go ├── iscsi.go └── testdata │ └── fixtures ├── kernel_random.go ├── kernel_random_test.go ├── loadavg.go ├── loadavg_test.go ├── mdstat.go ├── mdstat_test.go ├── meminfo.go ├── meminfo_test.go ├── mountinfo.go ├── mountinfo_test.go ├── mountstats.go ├── mountstats_test.go ├── net_conntrackstat.go ├── net_conntrackstat_test.go ├── net_dev.go ├── net_dev_snmp6.go ├── net_dev_snmp6_test.go ├── net_dev_test.go ├── net_ip_socket.go ├── net_ip_socket_test.go ├── net_protocols.go ├── net_protocols_test.go ├── net_route.go ├── net_route_test.go ├── net_sockstat.go ├── net_sockstat_test.go ├── net_softnet.go ├── net_softnet_test.go ├── net_tcp.go ├── net_tcp_test.go ├── net_tls_stat.go ├── net_tls_stat_test.go ├── net_udp.go ├── net_udp_test.go ├── net_unix.go ├── net_unix_test.go ├── net_wireless.go ├── net_wireless_test.go ├── net_xfrm.go ├── net_xfrm_test.go ├── netstat.go ├── netstat_test.go ├── nfs ├── nfs.go ├── parse.go ├── parse_nfs.go ├── parse_nfs_test.go ├── parse_nfsd.go └── parse_nfsd_test.go ├── proc.go ├── proc_cgroup.go ├── proc_cgroup_test.go ├── proc_cgroups.go ├── proc_cgroups_test.go ├── proc_environ.go ├── proc_environ_test.go ├── proc_fdinfo.go ├── proc_fdinfo_test.go ├── proc_interrupts.go ├── proc_interrupts_test.go ├── proc_io.go ├── proc_io_test.go ├── proc_limits.go ├── proc_limits_test.go ├── proc_maps.go ├── proc_maps32_test.go ├── proc_maps64_test.go ├── proc_netstat.go ├── proc_netstat_test.go ├── proc_ns.go ├── proc_ns_test.go ├── proc_psi.go ├── proc_psi_test.go ├── proc_smaps.go ├── proc_smaps_test.go ├── proc_snmp.go ├── proc_snmp6.go ├── proc_snmp6_test.go ├── proc_snmp_test.go ├── proc_stat.go ├── proc_stat_test.go ├── proc_status.go ├── proc_status_test.go ├── proc_sys.go ├── proc_sys_test.go ├── proc_test.go ├── schedstat.go ├── schedstat_test.go ├── scripts ├── check_build_tags.sh └── check_license.sh ├── selinuxfs ├── avc_cache_stats.go ├── avc_cache_stats_test.go ├── avc_hash_stats.go ├── avc_hash_stats_test.go ├── fs.go ├── fs_test.go └── testdata │ └── fixtures ├── slab.go ├── slab_test.go ├── softirqs.go ├── softirqs_test.go ├── stat.go ├── stat_test.go ├── swaps.go ├── swaps_test.go ├── sysfs ├── .gitignore ├── class_cooling_device.go ├── class_cooling_device_test.go ├── class_dmi.go ├── class_dmi_test.go ├── class_drm.go ├── class_drm_amdgpu.go ├── class_drm_amdgpu_test.go ├── class_drm_card.go ├── class_drm_card_test.go ├── class_fibrechannel.go ├── class_fibrechannel_test.go ├── class_infiniband.go ├── class_infiniband_test.go ├── class_nvme.go ├── class_nvme_test.go ├── class_power_supply.go ├── class_power_supply_test.go ├── class_powercap.go ├── class_powercap_test.go ├── class_sas_device.go ├── class_sas_device_test.go ├── class_sas_host.go ├── class_sas_host_test.go ├── class_sas_phy.go ├── class_sas_phy_test.go ├── class_sas_port.go ├── class_sas_port_test.go ├── class_scsitape.go ├── class_scsitape_test.go ├── class_thermal.go ├── class_thermal_test.go ├── class_watchdog.go ├── class_watchdog_test.go ├── clocksource.go ├── clocksource_test.go ├── doc.go ├── fs.go ├── fs_test.go ├── mdraid.go ├── mdraid_test.go ├── net_class.go ├── net_class_aer.go ├── net_class_aer_test.go ├── net_class_test.go ├── system_cpu.go ├── system_cpu_test.go ├── testdata │ └── fixtures ├── vmstat_numa.go ├── vmstat_numa_test.go ├── vulnerability.go └── vulnerability_test.go ├── testdata └── fixtures.ttar ├── thread.go ├── thread_test.go ├── ttar ├── vm.go ├── vm_test.go ├── xfs ├── parse.go ├── parse_test.go ├── testdata │ └── fixtures ├── xfs.go └── xfs_test.go ├── zoneinfo.go └── zoneinfo_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2.1 3 | 4 | jobs: 5 | lint: 6 | docker: 7 | - image: cimg/go:1.24 8 | steps: 9 | - checkout 10 | - run: make check_license 11 | - run: ./scripts/check_build_tags.sh 12 | - run: make fixtures 13 | - run: make update_fixtures 14 | - run: make style 15 | - run: git diff --exit-code 16 | 17 | test: 18 | parameters: 19 | go_version: 20 | type: string 21 | os: 22 | type: string 23 | run_test: 24 | type: boolean 25 | default: true 26 | docker: 27 | - image: cimg/go:<< parameters.go_version >> 28 | environment: 29 | GOOS: "<< parameters.os >>" 30 | steps: 31 | - checkout 32 | - run: make lint 33 | - when: 34 | condition: << parameters.run_test >> 35 | steps: 36 | - run: make test 37 | 38 | workflows: 39 | version: 2 40 | procfs: 41 | jobs: 42 | - lint 43 | - test: 44 | name: test-linux 45 | os: linux 46 | matrix: 47 | parameters: 48 | go_version: 49 | - "1.23" 50 | - "1.24" 51 | - test: 52 | name: test-windows 53 | os: windows 54 | run_test: false 55 | matrix: 56 | parameters: 57 | go_version: 58 | - "1.23" 59 | - "1.24" 60 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is synced from https://github.com/prometheus/prometheus 3 | name: golangci-lint 4 | on: 5 | push: 6 | paths: 7 | - "go.sum" 8 | - "go.mod" 9 | - "**.go" 10 | - "scripts/errcheck_excludes.txt" 11 | - ".github/workflows/golangci-lint.yml" 12 | - ".golangci.yml" 13 | pull_request: 14 | 15 | permissions: # added using https://github.com/step-security/secure-repo 16 | contents: read 17 | 18 | jobs: 19 | golangci: 20 | permissions: 21 | contents: read # for actions/checkout to fetch code 22 | pull-requests: read # for golangci/golangci-lint-action to fetch pull requests 23 | name: lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | - name: Install Go 29 | uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 30 | with: 31 | go-version: 1.24.x 32 | - name: Install snmp_exporter/generator dependencies 33 | run: sudo apt-get update && sudo apt-get -y install libsnmp-dev 34 | if: github.repository == 'prometheus/snmp_exporter' 35 | - name: Lint 36 | uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0 37 | with: 38 | args: --verbose 39 | version: v2.1.5 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /testdata/fixtures/ 2 | /fixtures 3 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | enable: 4 | - forbidigo 5 | - godot 6 | - misspell 7 | - revive 8 | - testifylint 9 | settings: 10 | forbidigo: 11 | forbid: 12 | - pattern: ^fmt\.Print.*$ 13 | msg: Do not commit print statements. 14 | godot: 15 | exclude: 16 | # Ignore "See: URL". 17 | - 'See:' 18 | capital: true 19 | misspell: 20 | locale: US 21 | exclusions: 22 | generated: lax 23 | presets: 24 | - comments 25 | - common-false-positives 26 | - legacy 27 | - std-error-handling 28 | paths: 29 | - third_party$ 30 | - builtin$ 31 | - examples$ 32 | formatters: 33 | enable: 34 | - gofmt 35 | - goimports 36 | settings: 37 | goimports: 38 | local-prefixes: 39 | - github.com/prometheus/procfs 40 | exclusions: 41 | generated: lax 42 | paths: 43 | - third_party$ 44 | - builtin$ 45 | - examples$ 46 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Prometheus Community Code of Conduct 2 | 3 | Prometheus follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). 4 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | * Johannes 'fish' Ziemke @discordianfish 2 | * Paul Gier @pgier 3 | * Ben Kochie @SuperQ 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Prometheus Authors 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | include Makefile.common 15 | 16 | %/.unpacked: %.ttar 17 | @echo ">> extracting fixtures $*" 18 | ./ttar -C $(dir $*) -x -f $*.ttar 19 | touch $@ 20 | 21 | fixtures: testdata/fixtures/.unpacked 22 | 23 | update_fixtures: 24 | rm -vf testdata/fixtures/.unpacked 25 | ./ttar -c -f testdata/fixtures.ttar -C testdata/ fixtures/ 26 | 27 | .PHONY: build 28 | build: 29 | 30 | .PHONY: test 31 | test: testdata/fixtures/.unpacked common-test 32 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | procfs provides functions to retrieve system, kernel and process 2 | metrics from the pseudo-filesystem proc. 3 | 4 | Copyright 2014-2015 The Prometheus Authors 5 | 6 | This product includes software developed at 7 | SoundCloud Ltd. (http://soundcloud.com/). 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # procfs 2 | 3 | This package provides functions to retrieve system, kernel, and process 4 | metrics from the pseudo-filesystems /proc and /sys. 5 | 6 | *WARNING*: This package is a work in progress. Its API may still break in 7 | backwards-incompatible ways without warnings. Use it at your own risk. 8 | 9 | [![Go Reference](https://pkg.go.dev/badge/github.com/prometheus/procfs.svg)](https://pkg.go.dev/github.com/prometheus/procfs) 10 | [![CircleCI](https://circleci.com/gh/prometheus/procfs/tree/master.svg?style=svg)](https://circleci.com/gh/prometheus/procfs/tree/master) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/procfs)](https://goreportcard.com/report/github.com/prometheus/procfs) 12 | 13 | ## Usage 14 | 15 | The procfs library is organized by packages based on whether the gathered data is coming from 16 | /proc, /sys, or both. Each package contains an `FS` type which represents the path to either /proc, 17 | /sys, or both. For example, cpu statistics are gathered from 18 | `/proc/stat` and are available via the root procfs package. First, the proc filesystem mount 19 | point is initialized, and then the stat information is read. 20 | 21 | ```go 22 | fs, err := procfs.NewFS("/proc") 23 | stats, err := fs.Stat() 24 | ``` 25 | 26 | Some sub-packages such as `blockdevice`, require access to both the proc and sys filesystems. 27 | 28 | ```go 29 | fs, err := blockdevice.NewFS("/proc", "/sys") 30 | stats, err := fs.ProcDiskstats() 31 | ``` 32 | 33 | ## Package Organization 34 | 35 | The packages in this project are organized according to (1) whether the data comes from the `/proc` or 36 | `/sys` filesystem and (2) the type of information being retrieved. For example, most process information 37 | can be gathered from the functions in the root `procfs` package. Information about block devices such as disk drives 38 | is available in the `blockdevices` sub-package. 39 | 40 | ## Building and Testing 41 | 42 | The procfs library is intended to be built as part of another application, so there are no distributable binaries. 43 | However, most of the API includes unit tests which can be run with `make test`. 44 | 45 | ### Updating Test Fixtures 46 | 47 | The procfs library includes a set of test fixtures which include many example files from 48 | the `/proc` and `/sys` filesystems. These fixtures are included as a [ttar](https://github.com/ideaship/ttar) file 49 | which is extracted automatically during testing. To add/update the test fixtures, first 50 | ensure the `testdata/fixtures` directory is up to date by removing the existing directory and then 51 | extracting the ttar file using `make testdata/fixtures/.unpacked` or just `make test`. 52 | 53 | ```bash 54 | rm -rf testdata/fixtures 55 | make test 56 | ``` 57 | 58 | Next, make the required changes to the extracted files in the `testdata/fixtures` directory. When 59 | the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file 60 | based on the updated `fixtures` directory. And finally, verify the changes using 61 | `git diff testdata/fixtures.ttar`. 62 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a security issue 2 | 3 | The Prometheus security policy, including how to report vulnerabilities, can be 4 | found here: 5 | 6 | 7 | -------------------------------------------------------------------------------- /arp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "net" 19 | "os" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | // Learned from include/uapi/linux/if_arp.h. 25 | const ( 26 | // Completed entry (ha valid). 27 | ATFComplete = 0x02 28 | // Permanent entry. 29 | ATFPermanent = 0x04 30 | // Publish entry. 31 | ATFPublish = 0x08 32 | // Has requested trailers. 33 | ATFUseTrailers = 0x10 34 | // Obsoleted: Want to use a netmask (only for proxy entries). 35 | ATFNetmask = 0x20 36 | // Don't answer this addresses. 37 | ATFDontPublish = 0x40 38 | ) 39 | 40 | // ARPEntry contains a single row of the columnar data represented in 41 | // /proc/net/arp. 42 | type ARPEntry struct { 43 | // IP address 44 | IPAddr net.IP 45 | // MAC address 46 | HWAddr net.HardwareAddr 47 | // Name of the device 48 | Device string 49 | // Flags 50 | Flags byte 51 | } 52 | 53 | // GatherARPEntries retrieves all the ARP entries, parse the relevant columns, 54 | // and then return a slice of ARPEntry's. 55 | func (fs FS) GatherARPEntries() ([]ARPEntry, error) { 56 | data, err := os.ReadFile(fs.proc.Path("net/arp")) 57 | if err != nil { 58 | return nil, fmt.Errorf("%w: error reading arp %s: %w", ErrFileRead, fs.proc.Path("net/arp"), err) 59 | } 60 | 61 | return parseARPEntries(data) 62 | } 63 | 64 | func parseARPEntries(data []byte) ([]ARPEntry, error) { 65 | lines := strings.Split(string(data), "\n") 66 | entries := make([]ARPEntry, 0) 67 | var err error 68 | const ( 69 | expectedDataWidth = 6 70 | expectedHeaderWidth = 9 71 | ) 72 | for _, line := range lines { 73 | columns := strings.Fields(line) 74 | width := len(columns) 75 | 76 | if width == expectedHeaderWidth || width == 0 { 77 | continue 78 | } else if width == expectedDataWidth { 79 | entry, err := parseARPEntry(columns) 80 | if err != nil { 81 | return []ARPEntry{}, fmt.Errorf("%w: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err) 82 | } 83 | entries = append(entries, entry) 84 | } else { 85 | return []ARPEntry{}, fmt.Errorf("%w: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err) 86 | } 87 | 88 | } 89 | 90 | return entries, err 91 | } 92 | 93 | func parseARPEntry(columns []string) (ARPEntry, error) { 94 | entry := ARPEntry{Device: columns[5]} 95 | ip := net.ParseIP(columns[0]) 96 | entry.IPAddr = ip 97 | 98 | if mac, err := net.ParseMAC(columns[3]); err == nil { 99 | entry.HWAddr = mac 100 | } else { 101 | return ARPEntry{}, err 102 | } 103 | 104 | if flags, err := strconv.ParseUint(columns[2], 0, 8); err == nil { 105 | entry.Flags = byte(flags) 106 | } else { 107 | return ARPEntry{}, err 108 | } 109 | 110 | return entry, nil 111 | } 112 | 113 | // IsComplete returns true if ARP entry is marked with complete flag. 114 | func (entry *ARPEntry) IsComplete() bool { 115 | return entry.Flags&ATFComplete != 0 116 | } 117 | -------------------------------------------------------------------------------- /arp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "net" 18 | "testing" 19 | ) 20 | 21 | func TestARP(t *testing.T) { 22 | fs, err := NewFS(procTestFixtures) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | arpFile, err := fs.GatherARPEntries() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | if want, got := "192.168.224.1", arpFile[0].IPAddr.String(); want != got { 33 | t.Errorf("want 192.168.224.1, got %s", got) 34 | } 35 | 36 | if want, got := "00:50:56:c0:00:08", arpFile[0].HWAddr.String(); want != got { 37 | t.Errorf("want %s, got %s", want, got) 38 | } 39 | 40 | if want, got := "ens33", arpFile[0].Device; want != got { 41 | t.Errorf("want ens33, got %s", got) 42 | } 43 | 44 | if want, got := true, arpFile[0].IsComplete(); want != got { 45 | t.Errorf("want %t, got %t", want, got) 46 | } 47 | 48 | if want, got := "192.168.224.2", arpFile[1].IPAddr.String(); want != got { 49 | t.Errorf("want 192.168.224.2, got %s", got) 50 | } 51 | 52 | if want, got := make(net.HardwareAddr, 6).String(), arpFile[1].HWAddr.String(); want != got { 53 | t.Errorf("expected empty MAC, got %s", got) 54 | } 55 | 56 | if want, got := "ens33", arpFile[1].Device; want != got { 57 | t.Errorf("want %s, got %s", want, got) 58 | } 59 | 60 | if want, got := byte(0x0), arpFile[1].Flags; want != got { 61 | t.Errorf("want %b, got %b", want, got) 62 | } 63 | 64 | if want, got := false, arpFile[1].IsComplete(); want != got { 65 | t.Errorf("want %t, got %t", want, got) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /bcache/bcache.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package bcache provides access to statistics exposed by the bcache (Linux 15 | // block cache). 16 | package bcache 17 | 18 | // Stats contains bcache runtime statistics, parsed from /sys/fs/bcache/. 19 | // 20 | // The names and meanings of each statistic were taken from bcache.txt and 21 | // files in drivers/md/bcache in the Linux kernel source. Counters are uint64 22 | // (in-kernel counters are mostly unsigned long). 23 | type Stats struct { 24 | // The name of the bcache used to source these statistics. 25 | Name string 26 | Bcache BcacheStats 27 | Bdevs []BdevStats 28 | Caches []CacheStats 29 | } 30 | 31 | // BcacheStats contains statistics tied to a bcache ID. 32 | type BcacheStats struct { // nolint:revive 33 | AverageKeySize uint64 34 | BtreeCacheSize uint64 35 | CacheAvailablePercent uint64 36 | Congested uint64 37 | RootUsagePercent uint64 38 | TreeDepth uint64 39 | Internal InternalStats 40 | FiveMin PeriodStats 41 | Total PeriodStats 42 | } 43 | 44 | // BdevStats contains statistics for one backing device. 45 | type BdevStats struct { 46 | Name string 47 | DirtyData uint64 48 | FiveMin PeriodStats 49 | Total PeriodStats 50 | WritebackRateDebug WritebackRateDebugStats 51 | } 52 | 53 | // CacheStats contains statistics for one cache device. 54 | type CacheStats struct { 55 | Name string 56 | IOErrors uint64 57 | MetadataWritten uint64 58 | Written uint64 59 | Priority PriorityStats 60 | } 61 | 62 | // PriorityStats contains statistics from the priority_stats file. 63 | type PriorityStats struct { 64 | UnusedPercent uint64 65 | MetadataPercent uint64 66 | } 67 | 68 | // InternalStats contains internal bcache statistics. 69 | type InternalStats struct { 70 | ActiveJournalEntries uint64 71 | BtreeNodes uint64 72 | BtreeReadAverageDurationNanoSeconds uint64 73 | CacheReadRaces uint64 74 | } 75 | 76 | // PeriodStats contains statistics for a time period (5 min or total). 77 | type PeriodStats struct { 78 | Bypassed uint64 79 | CacheBypassHits uint64 80 | CacheBypassMisses uint64 81 | CacheHits uint64 82 | CacheMissCollisions uint64 83 | CacheMisses uint64 84 | CacheReadaheads uint64 85 | } 86 | 87 | // WritebackRateDebugStats contains bcache writeback statistics. 88 | type WritebackRateDebugStats struct { 89 | Rate uint64 90 | Dirty uint64 91 | Target uint64 92 | Proportional int64 93 | Integral int64 94 | Change int64 95 | NextIO int64 96 | } 97 | -------------------------------------------------------------------------------- /bcache/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /blockdevice/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /btrfs/btrfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package btrfs provides access to statistics exposed by Btrfs filesystems. 15 | package btrfs 16 | 17 | // Stats contains statistics for a single Btrfs filesystem. 18 | // See Linux fs/btrfs/sysfs.c for more information. 19 | type Stats struct { 20 | UUID, Label string 21 | Allocation Allocation 22 | Devices map[string]*Device 23 | Features []string 24 | CloneAlignment uint64 25 | NodeSize uint64 26 | QuotaOverride uint64 27 | SectorSize uint64 28 | CommitStats CommitStats 29 | } 30 | 31 | // Allocation contains allocation statistics for data, metadata and system data. 32 | type Allocation struct { 33 | GlobalRsvReserved, GlobalRsvSize uint64 34 | Data, Metadata, System *AllocationStats 35 | } 36 | 37 | // AllocationStats contains allocation statistics for a data type. 38 | type AllocationStats struct { 39 | // Usage statistics 40 | DiskUsedBytes uint64 41 | DiskTotalBytes uint64 42 | MayUseBytes uint64 43 | PinnedBytes uint64 44 | TotalPinnedBytes uint64 45 | ReadOnlyBytes uint64 46 | ReservedBytes uint64 47 | UsedBytes uint64 48 | TotalBytes uint64 49 | 50 | // Flags marking filesystem state 51 | // See Linux fs/btrfs/ctree.h for more information. 52 | Flags uint64 53 | 54 | // Additional disk usage statistics depending on the disk layout. 55 | // At least one of these will exist and not be nil. 56 | Layouts map[string]*LayoutUsage 57 | } 58 | 59 | // LayoutUsage contains additional usage statistics for a disk layout. 60 | type LayoutUsage struct { 61 | UsedBytes, TotalBytes uint64 62 | Ratio float64 63 | } 64 | 65 | // Device contains information about a device that is part of a Btrfs filesystem. 66 | type Device struct { 67 | Size uint64 68 | } 69 | 70 | // Number of commits and various time related statistics. 71 | // See Linux fs/btrfs/sysfs.c with 6.x version. 72 | type CommitStats struct { 73 | Commits uint64 74 | LastCommitMs uint64 75 | MaxCommitMs uint64 76 | TotalCommitMs uint64 77 | } 78 | -------------------------------------------------------------------------------- /btrfs/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /buddyinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bufio" 18 | "fmt" 19 | "io" 20 | "os" 21 | "strconv" 22 | "strings" 23 | ) 24 | 25 | // A BuddyInfo is the details parsed from /proc/buddyinfo. 26 | // The data is comprised of an array of free fragments of each size. 27 | // The sizes are 2^n*PAGE_SIZE, where n is the array index. 28 | type BuddyInfo struct { 29 | Node string 30 | Zone string 31 | Sizes []float64 32 | } 33 | 34 | // BuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem. 35 | func (fs FS) BuddyInfo() ([]BuddyInfo, error) { 36 | file, err := os.Open(fs.proc.Path("buddyinfo")) 37 | if err != nil { 38 | return nil, err 39 | } 40 | defer file.Close() 41 | 42 | return parseBuddyInfo(file) 43 | } 44 | 45 | func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { 46 | var ( 47 | buddyInfo = []BuddyInfo{} 48 | scanner = bufio.NewScanner(r) 49 | bucketCount = -1 50 | ) 51 | 52 | for scanner.Scan() { 53 | var err error 54 | line := scanner.Text() 55 | parts := strings.Fields(line) 56 | 57 | if len(parts) < 4 { 58 | return nil, fmt.Errorf("%w: Invalid number of fields, found: %v", ErrFileParse, parts) 59 | } 60 | 61 | node := strings.TrimSuffix(parts[1], ",") 62 | zone := strings.TrimSuffix(parts[3], ",") 63 | arraySize := len(parts[4:]) 64 | 65 | if bucketCount == -1 { 66 | bucketCount = arraySize 67 | } else { 68 | if bucketCount != arraySize { 69 | return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize) 70 | } 71 | } 72 | 73 | sizes := make([]float64, arraySize) 74 | for i := 0; i < arraySize; i++ { 75 | sizes[i], err = strconv.ParseFloat(parts[i+4], 64) 76 | if err != nil { 77 | return nil, fmt.Errorf("%w: Invalid valid in buddyinfo: %f: %w", ErrFileParse, sizes[i], err) 78 | } 79 | } 80 | 81 | buddyInfo = append(buddyInfo, BuddyInfo{node, zone, sizes}) 82 | } 83 | 84 | return buddyInfo, scanner.Err() 85 | } 86 | -------------------------------------------------------------------------------- /buddyinfo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | func TestBuddyInfo(t *testing.T) { 22 | buddyInfo, err := getProcFixtures(t).BuddyInfo() 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | if want, got := "DMA", buddyInfo[0].Zone; want != got { 28 | t.Errorf("want Node 0, Zone %s, got %s", want, got) 29 | } 30 | 31 | if want, got := "Normal", buddyInfo[2].Zone; want != got { 32 | t.Errorf("want Node 0, Zone %s, got %s", want, got) 33 | } 34 | 35 | if want, got := 4381.0, buddyInfo[2].Sizes[0]; want != got { 36 | t.Errorf("want Node 0, Zone Normal %f, got %f", want, got) 37 | } 38 | 39 | if want, got := 572.0, buddyInfo[1].Sizes[1]; want != got { 40 | t.Errorf("want Node 0, Zone DMA32 %f, got %f", want, got) 41 | } 42 | } 43 | 44 | func TestParseBuddyInfoShort(t *testing.T) { 45 | 46 | testdata := `Node 0, zone 47 | Node 0, zone 48 | Node 0, zone 49 | ` 50 | reader := strings.NewReader(testdata) 51 | _, err := parseBuddyInfo(reader) 52 | if err == nil { 53 | t.Fatalf("expected error, but none occurred") 54 | } 55 | if want, got := "error parsing file: Invalid number of fields, found: [Node 0, zone]", err.Error(); want != got { 56 | t.Fatalf("error parsing file: Invalid number of fields, found: [Node %q, %q]", want, got) 57 | } 58 | } 59 | 60 | func TestParseBuddyInfoSizeMismatch(t *testing.T) { 61 | 62 | testdata := `Node 0, zone DMA 1 0 1 0 2 1 1 0 1 1 3 63 | Node 0, zone DMA32 759 572 791 475 194 45 12 0 0 0 0 0 64 | Node 0, zone Normal 4381 1093 185 1530 567 102 4 0 0 0 65 | ` 66 | reader := strings.NewReader(testdata) 67 | _, err := parseBuddyInfo(reader) 68 | if err == nil { 69 | t.Fatalf("expected error, but none occurred") 70 | } 71 | if want, got := "error parsing file: mismatch in number of buddyinfo buckets, previous count 11, new count 12", err.Error(); !strings.HasPrefix(got, want) { 72 | t.Fatalf("error parsing file: mismatch in number of buddyinfo buckets, previous count %q, new count %q", want, got) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /cmdline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "strings" 18 | 19 | "github.com/prometheus/procfs/internal/util" 20 | ) 21 | 22 | // CmdLine returns the command line of the kernel. 23 | func (fs FS) CmdLine() ([]string, error) { 24 | data, err := util.ReadFileNoStat(fs.proc.Path("cmdline")) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return strings.Fields(string(data)), nil 30 | } 31 | -------------------------------------------------------------------------------- /cmdline_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package procfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestCmdline(t *testing.T) { 26 | fs, err := NewFS(procTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.CmdLine() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | want := []string{ 37 | "BOOT_IMAGE=/vmlinuz-5.11.0-22-generic", 38 | "root=UUID=456a0345-450d-4f7b-b7c9-43e3241d99ad", 39 | "ro", 40 | "quiet", 41 | "splash", 42 | "vt.handoff=7", 43 | } 44 | 45 | if diff := cmp.Diff(want, got); diff != "" { 46 | t.Fatalf("unexpected CmdLine (-want +got):\n%s", diff) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cpuinfo_armx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && (arm || arm64) 15 | // +build linux 16 | // +build arm arm64 17 | 18 | package procfs 19 | 20 | var parseCPUInfo = parseCPUInfoARM 21 | -------------------------------------------------------------------------------- /cpuinfo_loong64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package procfs 18 | 19 | var parseCPUInfo = parseCPUInfoLoong 20 | -------------------------------------------------------------------------------- /cpuinfo_mipsx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && (mips || mipsle || mips64 || mips64le) 15 | // +build linux 16 | // +build mips mipsle mips64 mips64le 17 | 18 | package procfs 19 | 20 | var parseCPUInfo = parseCPUInfoMips 21 | -------------------------------------------------------------------------------- /cpuinfo_others.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x 15 | // +build linux,!386,!amd64,!arm,!arm64,!loong64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x 16 | 17 | package procfs 18 | 19 | var parseCPUInfo = parseCPUInfoDummy 20 | -------------------------------------------------------------------------------- /cpuinfo_ppcx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && (ppc64 || ppc64le) 15 | // +build linux 16 | // +build ppc64 ppc64le 17 | 18 | package procfs 19 | 20 | var parseCPUInfo = parseCPUInfoPPC 21 | -------------------------------------------------------------------------------- /cpuinfo_riscvx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && (riscv || riscv64) 15 | // +build linux 16 | // +build riscv riscv64 17 | 18 | package procfs 19 | 20 | var parseCPUInfo = parseCPUInfoRISCV 21 | -------------------------------------------------------------------------------- /cpuinfo_s390x.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package procfs 18 | 19 | var parseCPUInfo = parseCPUInfoS390X 20 | -------------------------------------------------------------------------------- /cpuinfo_x86.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && (386 || amd64) 15 | // +build linux 16 | // +build 386 amd64 17 | 18 | package procfs 19 | 20 | var parseCPUInfo = parseCPUInfoX86 21 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Prometheus Team 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package procfs provides functions to retrieve system, kernel and process 15 | // metrics from the pseudo-filesystem proc. 16 | // 17 | // Example: 18 | // 19 | // package main 20 | // 21 | // import ( 22 | // "fmt" 23 | // "log" 24 | // 25 | // "github.com/prometheus/procfs" 26 | // ) 27 | // 28 | // func main() { 29 | // p, err := procfs.Self() 30 | // if err != nil { 31 | // log.Fatalf("could not get process: %s", err) 32 | // } 33 | // 34 | // stat, err := p.Stat() 35 | // if err != nil { 36 | // log.Fatalf("could not get process stat: %s", err) 37 | // } 38 | // 39 | // fmt.Printf("command: %s\n", stat.Comm) 40 | // fmt.Printf("cpu time: %fs\n", stat.CPUTime()) 41 | // fmt.Printf("vsize: %dB\n", stat.VirtualMemory()) 42 | // fmt.Printf("rss: %dB\n", stat.ResidentMemory()) 43 | // } 44 | package procfs 45 | -------------------------------------------------------------------------------- /ext4/ext4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package btrfs provides access to statistics exposed by ext4 filesystems. 15 | package ext4 16 | 17 | import ( 18 | "path/filepath" 19 | "strings" 20 | 21 | "github.com/prometheus/procfs/internal/fs" 22 | "github.com/prometheus/procfs/internal/util" 23 | ) 24 | 25 | const ( 26 | sysFSPath = "fs" 27 | sysFSExt4Path = "ext4" 28 | ) 29 | 30 | // Stats contains statistics for a single Btrfs filesystem. 31 | // See Linux fs/btrfs/sysfs.c for more information. 32 | type Stats struct { 33 | Name string 34 | 35 | Errors uint64 36 | Warnings uint64 37 | Messages uint64 38 | } 39 | 40 | // FS represents the pseudo-filesystems proc and sys, which provides an 41 | // interface to kernel data structures. 42 | type FS struct { 43 | proc *fs.FS 44 | sys *fs.FS 45 | } 46 | 47 | // NewDefaultFS returns a new blockdevice fs using the default mountPoints for proc and sys. 48 | // It will error if either of these mount points can't be read. 49 | func NewDefaultFS() (FS, error) { 50 | return NewFS(fs.DefaultProcMountPoint, fs.DefaultSysMountPoint) 51 | } 52 | 53 | // NewFS returns a new XFS handle using the given proc and sys mountPoints. It will error 54 | // if either of the mounts point can't be read. 55 | func NewFS(procMountPoint string, sysMountPoint string) (FS, error) { 56 | if strings.TrimSpace(procMountPoint) == "" { 57 | procMountPoint = fs.DefaultProcMountPoint 58 | } 59 | procfs, err := fs.NewFS(procMountPoint) 60 | if err != nil { 61 | return FS{}, err 62 | } 63 | if strings.TrimSpace(sysMountPoint) == "" { 64 | sysMountPoint = fs.DefaultSysMountPoint 65 | } 66 | sysfs, err := fs.NewFS(sysMountPoint) 67 | if err != nil { 68 | return FS{}, err 69 | } 70 | return FS{&procfs, &sysfs}, nil 71 | } 72 | 73 | // ProcStat returns stats for the filesystem. 74 | func (fs FS) ProcStat() ([]*Stats, error) { 75 | matches, err := filepath.Glob(fs.sys.Path("fs/ext4/*")) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | stats := make([]*Stats, 0, len(matches)) 81 | for _, m := range matches { 82 | s := &Stats{} 83 | 84 | // "*" used in glob above indicates the name of the filesystem. 85 | name := filepath.Base(m) 86 | s.Name = name 87 | for file, p := range map[string]*uint64{ 88 | "errors_count": &s.Errors, 89 | "warning_count": &s.Warnings, 90 | "msg_count": &s.Messages, 91 | } { 92 | var val uint64 93 | val, err = util.ReadUintFromFile(fs.sys.Path(sysFSPath, sysFSExt4Path, name, file)) 94 | if err == nil { 95 | *p = val 96 | } 97 | } 98 | 99 | stats = append(stats, s) 100 | } 101 | 102 | return stats, nil 103 | } 104 | -------------------------------------------------------------------------------- /fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "github.com/prometheus/procfs/internal/fs" 18 | ) 19 | 20 | // FS represents the pseudo-filesystem sys, which provides an interface to 21 | // kernel data structures. 22 | type FS struct { 23 | proc fs.FS 24 | isReal bool 25 | } 26 | 27 | const ( 28 | // DefaultMountPoint is the common mount point of the proc filesystem. 29 | DefaultMountPoint = fs.DefaultProcMountPoint 30 | 31 | // SectorSize represents the size of a sector in bytes. 32 | // It is specific to Linux block I/O operations. 33 | SectorSize = 512 34 | ) 35 | 36 | // NewDefaultFS returns a new proc FS mounted under the default proc mountPoint. 37 | // It will error if the mount point directory can't be read or is a file. 38 | func NewDefaultFS() (FS, error) { 39 | return NewFS(DefaultMountPoint) 40 | } 41 | 42 | // NewFS returns a new proc FS mounted under the given proc mountPoint. It will error 43 | // if the mount point directory can't be read or is a file. 44 | func NewFS(mountPoint string) (FS, error) { 45 | fs, err := fs.NewFS(mountPoint) 46 | if err != nil { 47 | return FS{}, err 48 | } 49 | 50 | isReal, err := isRealProc(mountPoint) 51 | if err != nil { 52 | return FS{}, err 53 | } 54 | 55 | return FS{fs, isReal}, nil 56 | } 57 | -------------------------------------------------------------------------------- /fs_statfs_notype.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build !freebsd && !linux 15 | // +build !freebsd,!linux 16 | 17 | package procfs 18 | 19 | // isRealProc returns true on architectures that don't have a Type argument 20 | // in their Statfs_t struct. 21 | func isRealProc(_ string) (bool, error) { 22 | return true, nil 23 | } 24 | -------------------------------------------------------------------------------- /fs_statfs_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build freebsd || linux 15 | // +build freebsd linux 16 | 17 | package procfs 18 | 19 | import ( 20 | "syscall" 21 | ) 22 | 23 | // isRealProc determines whether supplied mountpoint is really a proc filesystem. 24 | func isRealProc(mountPoint string) (bool, error) { 25 | stat := syscall.Statfs_t{} 26 | err := syscall.Statfs(mountPoint, &stat) 27 | if err != nil { 28 | return false, err 29 | } 30 | 31 | // 0x9fa0 is PROC_SUPER_MAGIC: https://elixir.bootlin.com/linux/v6.1/source/include/uapi/linux/magic.h#L87 32 | return stat.Type == 0x9fa0, nil 33 | } 34 | -------------------------------------------------------------------------------- /fs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | const ( 19 | procTestFixtures = "testdata/fixtures/proc" 20 | ) 21 | 22 | func TestNewFS(t *testing.T) { 23 | if _, err := NewFS("foobar"); err == nil { 24 | t.Error("want NewFS to fail for non-existing mount point") 25 | } 26 | 27 | if _, err := NewFS("procfs.go"); err == nil { 28 | t.Error("want NewFS to fail if mount point is not a directory") 29 | } 30 | getProcFixtures(t) 31 | } 32 | 33 | func getProcFixtures(t *testing.T) FS { 34 | fs, err := NewFS(procTestFixtures) 35 | if err != nil { 36 | t.Fatalf("Creating pseudo fs from getProcFixtures failed at fixtures/proc with error: %s", err) 37 | } 38 | return fs 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/prometheus/procfs 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/google/go-cmp v0.7.0 7 | golang.org/x/sync v0.14.0 8 | golang.org/x/sys v0.33.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 2 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 3 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 4 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 5 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 6 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 7 | -------------------------------------------------------------------------------- /internal/fs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package fs 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "path/filepath" 20 | ) 21 | 22 | const ( 23 | // DefaultProcMountPoint is the common mount point of the proc filesystem. 24 | DefaultProcMountPoint = "/proc" 25 | 26 | // DefaultSysMountPoint is the common mount point of the sys filesystem. 27 | DefaultSysMountPoint = "/sys" 28 | 29 | // DefaultConfigfsMountPoint is the common mount point of the configfs. 30 | DefaultConfigfsMountPoint = "/sys/kernel/config" 31 | 32 | // DefaultSelinuxMountPoint is the common mount point of the selinuxfs. 33 | DefaultSelinuxMountPoint = "/sys/fs/selinux" 34 | ) 35 | 36 | // FS represents a pseudo-filesystem, normally /proc or /sys, which provides an 37 | // interface to kernel data structures. 38 | type FS string 39 | 40 | // NewFS returns a new FS mounted under the given mountPoint. It will error 41 | // if the mount point can't be read. 42 | func NewFS(mountPoint string) (FS, error) { 43 | info, err := os.Stat(mountPoint) 44 | if err != nil { 45 | return "", fmt.Errorf("could not read %q: %w", mountPoint, err) 46 | } 47 | if !info.IsDir() { 48 | return "", fmt.Errorf("mount point %q is not a directory", mountPoint) 49 | } 50 | 51 | return FS(mountPoint), nil 52 | } 53 | 54 | // Path appends the given path elements to the filesystem path, adding separators 55 | // as necessary. 56 | func (fs FS) Path(p ...string) string { 57 | return filepath.Join(append([]string{string(fs)}, p...)...) 58 | } 59 | -------------------------------------------------------------------------------- /internal/fs/fs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package fs 15 | 16 | import "testing" 17 | 18 | const ( 19 | sysTestFixtures = "testdata/fixtures/sys" 20 | ) 21 | 22 | func TestNewFS(t *testing.T) { 23 | if _, err := NewFS("foobar"); err == nil { 24 | t.Error("want NewFS to fail for non-existing mount point") 25 | } 26 | 27 | if _, err := NewFS("doc.go"); err == nil { 28 | t.Error("want NewFS to fail if mount point is not a directory") 29 | } 30 | 31 | if _, err := NewFS(sysTestFixtures); err != nil { 32 | t.Error("want NewFS to succeed if mount point exists") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/fs/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../../testdata/fixtures -------------------------------------------------------------------------------- /internal/util/readfile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package util 15 | 16 | import ( 17 | "io" 18 | "os" 19 | ) 20 | 21 | // ReadFileNoStat uses io.ReadAll to read contents of entire file. 22 | // This is similar to os.ReadFile but without the call to os.Stat, because 23 | // many files in /proc and /sys report incorrect file sizes (either 0 or 4096). 24 | // Reads a max file size of 1024kB. For files larger than this, a scanner 25 | // should be used. 26 | func ReadFileNoStat(filename string) ([]byte, error) { 27 | const maxBufferSize = 1024 * 1024 28 | 29 | f, err := os.Open(filename) 30 | if err != nil { 31 | return nil, err 32 | } 33 | defer f.Close() 34 | 35 | reader := io.LimitReader(f, maxBufferSize) 36 | return io.ReadAll(reader) 37 | } 38 | -------------------------------------------------------------------------------- /internal/util/sysreadfile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build (linux || darwin) && !appengine 15 | // +build linux darwin 16 | // +build !appengine 17 | 18 | package util 19 | 20 | import ( 21 | "bytes" 22 | "os" 23 | "strconv" 24 | "strings" 25 | "syscall" 26 | ) 27 | 28 | // SysReadFile is a simplified os.ReadFile that invokes syscall.Read directly. 29 | // https://github.com/prometheus/node_exporter/pull/728/files 30 | // 31 | // Note that this function will not read files larger than 128 bytes. 32 | func SysReadFile(file string) (string, error) { 33 | f, err := os.Open(file) 34 | if err != nil { 35 | return "", err 36 | } 37 | defer f.Close() 38 | 39 | // On some machines, hwmon drivers are broken and return EAGAIN. This causes 40 | // Go's os.ReadFile implementation to poll forever. 41 | // 42 | // Since we either want to read data or bail immediately, do the simplest 43 | // possible read using syscall directly. 44 | const sysFileBufferSize = 128 45 | b := make([]byte, sysFileBufferSize) 46 | n, err := syscall.Read(int(f.Fd()), b) 47 | if err != nil { 48 | return "", err 49 | } 50 | 51 | return string(bytes.TrimSpace(b[:n])), nil 52 | } 53 | 54 | // SysReadUintFromFile reads a file using SysReadFile and attempts to parse a uint64 from it. 55 | func SysReadUintFromFile(path string) (uint64, error) { 56 | data, err := SysReadFile(path) 57 | if err != nil { 58 | return 0, err 59 | } 60 | return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) 61 | } 62 | 63 | // SysReadIntFromFile reads a file using SysReadFile and attempts to parse a int64 from it. 64 | func SysReadIntFromFile(path string) (int64, error) { 65 | data, err := SysReadFile(path) 66 | if err != nil { 67 | return 0, err 68 | } 69 | return strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64) 70 | } 71 | -------------------------------------------------------------------------------- /internal/util/sysreadfile_compat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build (linux && appengine) || (!linux && !darwin) 15 | // +build linux,appengine !linux,!darwin 16 | 17 | package util 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | // SysReadFile is here implemented as a noop for builds that do not support 24 | // the read syscall. For example Windows, or Linux on Google App Engine. 25 | func SysReadFile(file string) (string, error) { 26 | return "", fmt.Errorf("not supported on this platform") 27 | } 28 | -------------------------------------------------------------------------------- /internal/util/valueparser.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package util 15 | 16 | import ( 17 | "strconv" 18 | ) 19 | 20 | // TODO(mdlayher): util packages are an anti-pattern and this should be moved 21 | // somewhere else that is more focused in the future. 22 | 23 | // A ValueParser enables parsing a single string into a variety of data types 24 | // in a concise and safe way. The Err method must be invoked after invoking 25 | // any other methods to ensure a value was successfully parsed. 26 | type ValueParser struct { 27 | v string 28 | err error 29 | } 30 | 31 | // NewValueParser creates a ValueParser using the input string. 32 | func NewValueParser(v string) *ValueParser { 33 | return &ValueParser{v: v} 34 | } 35 | 36 | // Int interprets the underlying value as an int and returns that value. 37 | func (vp *ValueParser) Int() int { return int(vp.int64()) } 38 | 39 | // PInt64 interprets the underlying value as an int64 and returns a pointer to 40 | // that value. 41 | func (vp *ValueParser) PInt64() *int64 { 42 | if vp.err != nil { 43 | return nil 44 | } 45 | 46 | v := vp.int64() 47 | return &v 48 | } 49 | 50 | // int64 interprets the underlying value as an int64 and returns that value. 51 | // TODO: export if/when necessary. 52 | func (vp *ValueParser) int64() int64 { 53 | if vp.err != nil { 54 | return 0 55 | } 56 | 57 | // A base value of zero makes ParseInt infer the correct base using the 58 | // string's prefix, if any. 59 | const base = 0 60 | v, err := strconv.ParseInt(vp.v, base, 64) 61 | if err != nil { 62 | vp.err = err 63 | return 0 64 | } 65 | 66 | return v 67 | } 68 | 69 | // PUInt64 interprets the underlying value as an uint64 and returns a pointer to 70 | // that value. 71 | func (vp *ValueParser) PUInt64() *uint64 { 72 | if vp.err != nil { 73 | return nil 74 | } 75 | 76 | // A base value of zero makes ParseInt infer the correct base using the 77 | // string's prefix, if any. 78 | const base = 0 79 | v, err := strconv.ParseUint(vp.v, base, 64) 80 | if err != nil { 81 | vp.err = err 82 | return nil 83 | } 84 | 85 | return &v 86 | } 87 | 88 | // Err returns the last error, if any, encountered by the ValueParser. 89 | func (vp *ValueParser) Err() error { 90 | return vp.err 91 | } 92 | -------------------------------------------------------------------------------- /iscsi/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /kernel_random.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build !windows 15 | // +build !windows 16 | 17 | package procfs 18 | 19 | import ( 20 | "os" 21 | 22 | "github.com/prometheus/procfs/internal/util" 23 | ) 24 | 25 | // KernelRandom contains information about to the kernel's random number generator. 26 | type KernelRandom struct { 27 | // EntropyAvaliable gives the available entropy, in bits. 28 | EntropyAvaliable *uint64 29 | // PoolSize gives the size of the entropy pool, in bits. 30 | PoolSize *uint64 31 | // URandomMinReseedSeconds is the number of seconds after which the DRNG will be reseeded. 32 | URandomMinReseedSeconds *uint64 33 | // WriteWakeupThreshold the number of bits of entropy below which we wake up processes 34 | // that do a select(2) or poll(2) for write access to /dev/random. 35 | WriteWakeupThreshold *uint64 36 | // ReadWakeupThreshold is the number of bits of entropy required for waking up processes that sleep 37 | // waiting for entropy from /dev/random. 38 | ReadWakeupThreshold *uint64 39 | } 40 | 41 | // KernelRandom returns values from /proc/sys/kernel/random. 42 | func (fs FS) KernelRandom() (KernelRandom, error) { 43 | random := KernelRandom{} 44 | 45 | for file, p := range map[string]**uint64{ 46 | "entropy_avail": &random.EntropyAvaliable, 47 | "poolsize": &random.PoolSize, 48 | "urandom_min_reseed_secs": &random.URandomMinReseedSeconds, 49 | "write_wakeup_threshold": &random.WriteWakeupThreshold, 50 | "read_wakeup_threshold": &random.ReadWakeupThreshold, 51 | } { 52 | val, err := util.ReadUintFromFile(fs.proc.Path("sys", "kernel", "random", file)) 53 | if os.IsNotExist(err) { 54 | continue 55 | } 56 | if err != nil { 57 | return random, err 58 | } 59 | *p = &val 60 | } 61 | 62 | return random, nil 63 | } 64 | -------------------------------------------------------------------------------- /kernel_random_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build !windows 15 | // +build !windows 16 | 17 | package procfs 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | const procfsFixtures = "testdata/fixtures/proc" 24 | 25 | func TestKernelRandom(t *testing.T) { 26 | fs, err := NewFS(procfsFixtures) 27 | if err != nil { 28 | t.Fatalf("failed to access %s: %v", procfsFixtures, err) 29 | } 30 | 31 | random, err := fs.KernelRandom() 32 | if err != nil { 33 | t.Fatalf("failed to collect %s/sys/kernel/random: %v", procfsFixtures, err) 34 | } 35 | 36 | if *random.EntropyAvaliable != 3943 { 37 | t.Errorf("entropy_avail, want %d got %d", 3943, *random.EntropyAvaliable) 38 | } 39 | if *random.PoolSize != 4096 { 40 | t.Errorf("poolsize, want %d got %d", 4096, *random.PoolSize) 41 | } 42 | if *random.URandomMinReseedSeconds != 60 { 43 | t.Errorf("urandom_min_reseed_secs, want %d got %d", 60, *random.URandomMinReseedSeconds) 44 | } 45 | if *random.WriteWakeupThreshold != 3072 { 46 | t.Errorf("write_wakeup_threshold, want %d got %d", 3072, *random.WriteWakeupThreshold) 47 | } 48 | if random.ReadWakeupThreshold != nil { 49 | t.Errorf("read_wakeup_threshold, want %v got %d", nil, *random.ReadWakeupThreshold) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /loadavg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "strconv" 19 | "strings" 20 | 21 | "github.com/prometheus/procfs/internal/util" 22 | ) 23 | 24 | // LoadAvg represents an entry in /proc/loadavg. 25 | type LoadAvg struct { 26 | Load1 float64 27 | Load5 float64 28 | Load15 float64 29 | } 30 | 31 | // LoadAvg returns loadavg from /proc. 32 | func (fs FS) LoadAvg() (*LoadAvg, error) { 33 | path := fs.proc.Path("loadavg") 34 | 35 | data, err := util.ReadFileNoStat(path) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return parseLoad(data) 40 | } 41 | 42 | // Parse /proc loadavg and return 1m, 5m and 15m. 43 | func parseLoad(loadavgBytes []byte) (*LoadAvg, error) { 44 | loads := make([]float64, 3) 45 | parts := strings.Fields(string(loadavgBytes)) 46 | if len(parts) < 3 { 47 | return nil, fmt.Errorf("%w: Malformed line %q", ErrFileParse, string(loadavgBytes)) 48 | } 49 | 50 | var err error 51 | for i, load := range parts[0:3] { 52 | loads[i], err = strconv.ParseFloat(load, 64) 53 | if err != nil { 54 | return nil, fmt.Errorf("%w: Cannot parse load: %f: %w", ErrFileParse, loads[i], err) 55 | } 56 | } 57 | return &LoadAvg{ 58 | Load1: loads[0], 59 | Load5: loads[1], 60 | Load15: loads[2], 61 | }, nil 62 | } 63 | -------------------------------------------------------------------------------- /loadavg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | ) 21 | 22 | func TestLoadAvg(t *testing.T) { 23 | fs, err := NewFS(procTestFixtures) 24 | if err != nil { 25 | t.Fatalf("failed to open procfs: %v", err) 26 | } 27 | 28 | loadavg, err := fs.LoadAvg() 29 | if err != nil { 30 | t.Fatalf("failed to get loadavg: %v", err) 31 | } 32 | 33 | if diff := cmp.Diff(0.02, loadavg.Load1); diff != "" { 34 | t.Fatalf("unexpected LoadAvg Per a minute:\n%s", diff) 35 | } 36 | if diff := cmp.Diff(0.04, loadavg.Load5); diff != "" { 37 | t.Fatalf("unexpected LoadAvg Per five minutes:\n%s", diff) 38 | } 39 | if diff := cmp.Diff(0.05, loadavg.Load15); diff != "" { 40 | t.Fatalf("unexpected LoadAvg Per fifteen minutes:\n%s", diff) 41 | } 42 | } 43 | 44 | func Test_parseLoad(t *testing.T) { 45 | tests := []struct { 46 | name string 47 | s string 48 | ok bool 49 | loadavg *LoadAvg 50 | }{ 51 | { 52 | name: "empty", 53 | ok: false, 54 | }, 55 | { 56 | name: "not enough fields", 57 | s: `0.00 0.03`, 58 | ok: false, 59 | }, 60 | { 61 | name: "invalid line", 62 | s: `malformed line`, 63 | ok: false, 64 | }, 65 | { 66 | name: "valid line", 67 | s: `0.00 0.03 0.05 1/502 33634`, 68 | ok: true, 69 | loadavg: &LoadAvg{Load1: 0, Load5: 0.03, Load15: 0.05}, 70 | }, 71 | } 72 | 73 | for _, tt := range tests { 74 | t.Run(tt.name, func(t *testing.T) { 75 | loadavg, err := parseLoad([]byte(tt.s)) 76 | if err != nil { 77 | if tt.ok { 78 | t.Fatalf("failed to parse loadavg: %v", err) 79 | } 80 | 81 | t.Logf("OK error: %v", err) 82 | return 83 | } 84 | if !tt.ok { 85 | t.Fatal("expected an error, but none occurred") 86 | } 87 | 88 | if diff := cmp.Diff(tt.loadavg, loadavg); diff != "" { 89 | t.Errorf("unexpected loadavg(-want +got):\n%s", diff) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /net_dev_snmp6.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bufio" 18 | "errors" 19 | "io" 20 | "os" 21 | "strconv" 22 | "strings" 23 | ) 24 | 25 | // NetDevSNMP6 is parsed from files in /proc/net/dev_snmp6/ or /proc//net/dev_snmp6/. 26 | // The outer map's keys are interface names and the inner map's keys are stat names. 27 | // 28 | // If you'd like a total across all interfaces, please use the Snmp6() method of the Proc type. 29 | type NetDevSNMP6 map[string]map[string]uint64 30 | 31 | // Returns kernel/system statistics read from interface files within the /proc/net/dev_snmp6/ 32 | // directory. 33 | func (fs FS) NetDevSNMP6() (NetDevSNMP6, error) { 34 | return newNetDevSNMP6(fs.proc.Path("net/dev_snmp6")) 35 | } 36 | 37 | // Returns kernel/system statistics read from interface files within the /proc//net/dev_snmp6/ 38 | // directory. 39 | func (p Proc) NetDevSNMP6() (NetDevSNMP6, error) { 40 | return newNetDevSNMP6(p.path("net/dev_snmp6")) 41 | } 42 | 43 | // newNetDevSNMP6 creates a new NetDevSNMP6 from the contents of the given directory. 44 | func newNetDevSNMP6(dir string) (NetDevSNMP6, error) { 45 | netDevSNMP6 := make(NetDevSNMP6) 46 | 47 | // The net/dev_snmp6 folders contain one file per interface 48 | ifaceFiles, err := os.ReadDir(dir) 49 | if err != nil { 50 | // On systems with IPv6 disabled, this directory won't exist. 51 | // Do nothing. 52 | if errors.Is(err, os.ErrNotExist) { 53 | return netDevSNMP6, err 54 | } 55 | return netDevSNMP6, err 56 | } 57 | 58 | for _, iFaceFile := range ifaceFiles { 59 | f, err := os.Open(dir + "/" + iFaceFile.Name()) 60 | if err != nil { 61 | return netDevSNMP6, err 62 | } 63 | defer f.Close() 64 | 65 | netDevSNMP6[iFaceFile.Name()], err = parseNetDevSNMP6Stats(f) 66 | if err != nil { 67 | return netDevSNMP6, err 68 | } 69 | } 70 | 71 | return netDevSNMP6, nil 72 | } 73 | 74 | func parseNetDevSNMP6Stats(r io.Reader) (map[string]uint64, error) { 75 | m := make(map[string]uint64) 76 | 77 | scanner := bufio.NewScanner(r) 78 | for scanner.Scan() { 79 | stat := strings.Fields(scanner.Text()) 80 | if len(stat) < 2 { 81 | continue 82 | } 83 | key, val := stat[0], stat[1] 84 | 85 | // Expect stat name to contain "6" or be "ifIndex" 86 | if strings.Contains(key, "6") || key == "ifIndex" { 87 | v, err := strconv.ParseUint(val, 10, 64) 88 | if err != nil { 89 | return m, err 90 | } 91 | 92 | m[key] = v 93 | } 94 | } 95 | return m, scanner.Err() 96 | } 97 | -------------------------------------------------------------------------------- /net_dev_snmp6_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "testing" 19 | ) 20 | 21 | func TestNetDevSNMP6(t *testing.T) { 22 | fs, err := NewFS(procTestFixtures) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | netDevSNMP6, err := fs.NetDevSNMP6() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | if err := validateNetDevSNMP6(netDevSNMP6); err != nil { 33 | t.Error(err.Error()) 34 | } 35 | } 36 | 37 | func TestProcNetDevSNMP6(t *testing.T) { 38 | p, err := getProcFixtures(t).Proc(26231) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | procNetDevSNMP6, err := p.NetDevSNMP6() 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | 48 | if err := validateNetDevSNMP6(procNetDevSNMP6); err != nil { 49 | t.Error(err.Error()) 50 | } 51 | } 52 | 53 | func validateNetDevSNMP6(have NetDevSNMP6) error { 54 | var wantNetDevSNMP6 = map[string]map[string]uint64{ 55 | "eth0": { 56 | "ifIndex": 1, 57 | "Ip6InOctets": 14064059261, 58 | "Ip6OutOctets": 811213622, 59 | "Icmp6InMsgs": 53293, 60 | "Icmp6OutMsgs": 20400, 61 | }, 62 | "eth1": { 63 | "ifIndex": 2, 64 | "Ip6InOctets": 303177290674, 65 | "Ip6OutOctets": 29245052746, 66 | "Icmp6InMsgs": 37911, 67 | "Icmp6OutMsgs": 114015, 68 | }, 69 | } 70 | 71 | for wantIface, wantData := range wantNetDevSNMP6 { 72 | if haveData, ok := have[wantIface]; ok { 73 | for wantStat, wantVal := range wantData { 74 | if haveVal, ok := haveData[wantStat]; !ok { 75 | return fmt.Errorf("stat %s missing from %s test data", wantStat, wantIface) 76 | } else if wantVal != haveVal { 77 | return fmt.Errorf("%s - %s: want %d, have %d", wantIface, wantStat, wantVal, haveVal) 78 | } 79 | } 80 | } else { 81 | return fmt.Errorf("%s not found in test data", wantIface) 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /net_dev_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "testing" 19 | ) 20 | 21 | func TestNetDevParseLine(t *testing.T) { 22 | tc := []string{"eth0", "eth0:1"} 23 | for i := range tc { 24 | rawLine := fmt.Sprintf(` %v: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16`, tc[i]) 25 | have, err := NetDev{}.parseLine(rawLine) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | want := NetDevLine{tc[i], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 30 | if want != *have { 31 | t.Errorf("want %v, have %v", want, have) 32 | } 33 | } 34 | 35 | } 36 | 37 | func TestNetDev(t *testing.T) { 38 | fs, err := NewFS(procTestFixtures) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | netDev, err := fs.NetDev() 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | 48 | lines := map[string]NetDevLine{ 49 | "vethf345468": {Name: "vethf345468", RxBytes: 648, RxPackets: 8, TxBytes: 438, TxPackets: 5}, 50 | "lo": {Name: "lo", RxBytes: 1664039048, RxPackets: 1566805, TxBytes: 1664039048, TxPackets: 1566805}, 51 | "docker0": {Name: "docker0", RxBytes: 2568, RxPackets: 38, TxBytes: 438, TxPackets: 5}, 52 | "eth0": {Name: "eth0", RxBytes: 874354587, RxPackets: 1036395, TxBytes: 563352563, TxPackets: 732147}, 53 | } 54 | 55 | if want, have := len(lines), len(netDev); want != have { 56 | t.Errorf("want %d parsed net/dev lines, have %d", want, have) 57 | } 58 | for _, line := range netDev { 59 | if want, have := lines[line.Name], line; want != have { 60 | t.Errorf("%s: want %v, have %v", line.Name, want, have) 61 | } 62 | } 63 | } 64 | 65 | func TestProcNetDev(t *testing.T) { 66 | p, err := getProcFixtures(t).Proc(26231) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | netDev, err := p.NetDev() 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | 76 | lines := map[string]NetDevLine{ 77 | "lo": {Name: "lo"}, 78 | "eth0": {Name: "eth0", RxBytes: 438, RxPackets: 5, TxBytes: 648, TxPackets: 8}, 79 | } 80 | 81 | if want, have := len(lines), len(netDev); want != have { 82 | t.Errorf("want %d parsed net/dev lines, have %d", want, have) 83 | } 84 | for _, line := range netDev { 85 | if want, have := lines[line.Name], line; want != have { 86 | t.Errorf("%s: want %v, have %v", line.Name, want, have) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /net_route_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bytes" 18 | "reflect" 19 | "testing" 20 | ) 21 | 22 | func TestParseNetRoute(t *testing.T) { 23 | var netRoute = []byte(`Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT 24 | eno16780032 00000000 9503A8C0 0003 0 0 100 00000000 0 0 0 25 | eno16780032 0000A8C0 00000000 0001 0 0 100 0000FFFF 0 0 0`) 26 | 27 | r := bytes.NewReader(netRoute) 28 | parsed, _ := parseNetRoute(r) 29 | want := []NetRouteLine{ 30 | { 31 | Iface: "eno16780032", 32 | Destination: 0, 33 | Gateway: 2500044992, 34 | Flags: 3, 35 | RefCnt: 0, 36 | Use: 0, 37 | Metric: 100, 38 | Mask: 0, 39 | MTU: 0, 40 | Window: 0, 41 | IRTT: 0, 42 | }, 43 | { 44 | Iface: "eno16780032", 45 | Destination: 43200, 46 | Gateway: 0, 47 | Flags: 1, 48 | RefCnt: 0, 49 | Use: 0, 50 | Metric: 100, 51 | Mask: 65535, 52 | MTU: 0, 53 | Window: 0, 54 | IRTT: 0, 55 | }, 56 | } 57 | if !reflect.DeepEqual(want, parsed) { 58 | t.Errorf("want %v, parsed %v", want, parsed) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /net_softnet_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | ) 21 | 22 | func TestNetSoftnet(t *testing.T) { 23 | fs, err := NewFS(procTestFixtures) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | want := []SoftnetStat{ 29 | { 30 | Processed: 0x00358fe3, 31 | Dropped: 0x00006283, 32 | TimeSqueezed: 0x00000000, 33 | CPUCollision: 0x00000000, 34 | ReceivedRps: 0x000855fc, 35 | FlowLimitCount: 0x00000076, 36 | SoftnetBacklogLen: 0x00000000, 37 | Index: 0x00000000, 38 | Width: 13, 39 | }, 40 | { 41 | Processed: 0x00953d1a, 42 | Dropped: 0x00000446, 43 | TimeSqueezed: 0x000000b1, 44 | CPUCollision: 0x00000000, 45 | ReceivedRps: 0x008eeb9a, 46 | FlowLimitCount: 0x0000002b, 47 | SoftnetBacklogLen: 0x000000dc, 48 | Index: 0x00000001, 49 | Width: 13, 50 | }, 51 | { 52 | Processed: 0x00015c73, 53 | Dropped: 0x00020e76, 54 | TimeSqueezed: 0xf0000769, 55 | CPUCollision: 0x00000004, 56 | ReceivedRps: 0x00000003, 57 | FlowLimitCount: 0x00000002, 58 | Index: 0x00000002, 59 | Width: 11, 60 | }, 61 | { 62 | Processed: 0x01663fb2, 63 | Dropped: 0x00000000, 64 | TimeSqueezed: 0x0109a4, 65 | CPUCollision: 0x00020e76, 66 | Index: 0x00000003, 67 | Width: 9, 68 | }, 69 | { 70 | Processed: 0x00008e78, 71 | Dropped: 0x00000001, 72 | TimeSqueezed: 0x00000011, 73 | CPUCollision: 0x00000020, 74 | ReceivedRps: 0x00000010, 75 | Index: 0x00000004, 76 | Width: 10, 77 | }, 78 | } 79 | 80 | got, err := fs.NetSoftnetStat() 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | 85 | if diff := cmp.Diff(want, got); diff != "" { 86 | t.Fatalf("unexpected softnet stats(-want +got):\n%s", diff) 87 | } 88 | } 89 | 90 | func TestBadSoftnet(t *testing.T) { 91 | softNetProcFile = "net/softnet_stat.broken" 92 | fs, err := NewFS(procTestFixtures) 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | 97 | _, err = fs.NetSoftnetStat() 98 | if err == nil { 99 | t.Fatal("expected error, got nil") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /net_tcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | type ( 17 | // NetTCP represents the contents of /proc/net/tcp{,6} file without the header. 18 | NetTCP []*netIPSocketLine 19 | 20 | // NetTCPSummary provides already computed values like the total queue lengths or 21 | // the total number of used sockets. In contrast to NetTCP it does not collect 22 | // the parsed lines into a slice. 23 | NetTCPSummary NetIPSocketSummary 24 | ) 25 | 26 | // NetTCP returns the IPv4 kernel/networking statistics for TCP datagrams 27 | // read from /proc/net/tcp. 28 | // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. 29 | func (fs FS) NetTCP() (NetTCP, error) { 30 | return newNetTCP(fs.proc.Path("net/tcp")) 31 | } 32 | 33 | // NetTCP6 returns the IPv6 kernel/networking statistics for TCP datagrams 34 | // read from /proc/net/tcp6. 35 | // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. 36 | func (fs FS) NetTCP6() (NetTCP, error) { 37 | return newNetTCP(fs.proc.Path("net/tcp6")) 38 | } 39 | 40 | // NetTCPSummary returns already computed statistics like the total queue lengths 41 | // for TCP datagrams read from /proc/net/tcp. 42 | // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. 43 | func (fs FS) NetTCPSummary() (*NetTCPSummary, error) { 44 | return newNetTCPSummary(fs.proc.Path("net/tcp")) 45 | } 46 | 47 | // NetTCP6Summary returns already computed statistics like the total queue lengths 48 | // for TCP datagrams read from /proc/net/tcp6. 49 | // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. 50 | func (fs FS) NetTCP6Summary() (*NetTCPSummary, error) { 51 | return newNetTCPSummary(fs.proc.Path("net/tcp6")) 52 | } 53 | 54 | // newNetTCP creates a new NetTCP{,6} from the contents of the given file. 55 | func newNetTCP(file string) (NetTCP, error) { 56 | n, err := newNetIPSocket(file) 57 | n1 := NetTCP(n) 58 | return n1, err 59 | } 60 | 61 | func newNetTCPSummary(file string) (*NetTCPSummary, error) { 62 | n, err := newNetIPSocketSummary(file) 63 | if n == nil { 64 | return nil, err 65 | } 66 | n1 := NetTCPSummary(*n) 67 | return &n1, err 68 | } 69 | -------------------------------------------------------------------------------- /net_tls_stat_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Prometheus Team 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | func TestTLSStat(t *testing.T) { 21 | tlsStats, err := getProcFixtures(t).NewTLSStat() 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | for _, test := range []struct { 27 | name string 28 | want int 29 | got int 30 | }{ 31 | {name: "TLSCurrTxSw", want: 5, got: tlsStats.TLSCurrTxSw}, 32 | {name: "TLSCurrRxSw", want: 5, got: tlsStats.TLSCurrRxSw}, 33 | {name: "TLSCurrTxDevice", want: 0, got: tlsStats.TLSCurrTxDevice}, 34 | {name: "TLSCurrRxDevice", want: 0, got: tlsStats.TLSCurrRxDevice}, 35 | {name: "TLSTxSw", want: 8711, got: tlsStats.TLSTxSw}, 36 | {name: "TLSTxSw", want: 8711, got: tlsStats.TLSRxSw}, 37 | {name: "TLSTxDevice", want: 0, got: tlsStats.TLSTxDevice}, 38 | {name: "TLSRxDevice", want: 0, got: tlsStats.TLSRxDevice}, 39 | {name: "TLSDecryptError", want: 13, got: tlsStats.TLSDecryptError}, 40 | {name: "TLSRxDeviceResync", want: 0, got: tlsStats.TLSRxDeviceResync}, 41 | {name: "TLSDecryptRetry", want: 0, got: tlsStats.TLSDecryptRetry}, 42 | {name: "TLSRxNoPadViolation", want: 0, got: tlsStats.TLSRxNoPadViolation}, 43 | } { 44 | if test.want != test.got { 45 | t.Errorf("Want %s %d, have %d", test.name, test.want, test.got) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /net_udp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | type ( 17 | // NetUDP represents the contents of /proc/net/udp{,6} file without the header. 18 | NetUDP []*netIPSocketLine 19 | 20 | // NetUDPSummary provides already computed values like the total queue lengths or 21 | // the total number of used sockets. In contrast to NetUDP it does not collect 22 | // the parsed lines into a slice. 23 | NetUDPSummary NetIPSocketSummary 24 | ) 25 | 26 | // NetUDP returns the IPv4 kernel/networking statistics for UDP datagrams 27 | // read from /proc/net/udp. 28 | func (fs FS) NetUDP() (NetUDP, error) { 29 | return newNetUDP(fs.proc.Path("net/udp")) 30 | } 31 | 32 | // NetUDP6 returns the IPv6 kernel/networking statistics for UDP datagrams 33 | // read from /proc/net/udp6. 34 | func (fs FS) NetUDP6() (NetUDP, error) { 35 | return newNetUDP(fs.proc.Path("net/udp6")) 36 | } 37 | 38 | // NetUDPSummary returns already computed statistics like the total queue lengths 39 | // for UDP datagrams read from /proc/net/udp. 40 | func (fs FS) NetUDPSummary() (*NetUDPSummary, error) { 41 | return newNetUDPSummary(fs.proc.Path("net/udp")) 42 | } 43 | 44 | // NetUDP6Summary returns already computed statistics like the total queue lengths 45 | // for UDP datagrams read from /proc/net/udp6. 46 | func (fs FS) NetUDP6Summary() (*NetUDPSummary, error) { 47 | return newNetUDPSummary(fs.proc.Path("net/udp6")) 48 | } 49 | 50 | // newNetUDP creates a new NetUDP{,6} from the contents of the given file. 51 | func newNetUDP(file string) (NetUDP, error) { 52 | n, err := newNetIPSocket(file) 53 | n1 := NetUDP(n) 54 | return n1, err 55 | } 56 | 57 | func newNetUDPSummary(file string) (*NetUDPSummary, error) { 58 | n, err := newNetIPSocketSummary(file) 59 | if n == nil { 60 | return nil, err 61 | } 62 | n1 := NetUDPSummary(*n) 63 | return &n1, err 64 | } 65 | -------------------------------------------------------------------------------- /net_wireless_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "reflect" 18 | "testing" 19 | ) 20 | 21 | func TestWireless(t *testing.T) { 22 | fs, err := NewFS(procTestFixtures) 23 | if err != nil { 24 | t.Fatalf("failed to open procfs: %v", err) 25 | } 26 | 27 | got, err := fs.Wireless() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | expected := []*Wireless{ 33 | { 34 | Name: "wlan0", 35 | Status: 1, 36 | QualityLink: 2, 37 | QualityLevel: 3, 38 | QualityNoise: 4, 39 | DiscardedNwid: 5, 40 | DiscardedCrypt: 6, 41 | DiscardedFrag: 7, 42 | DiscardedRetry: 8, 43 | DiscardedMisc: 9, 44 | MissedBeacon: 10, 45 | }, 46 | { 47 | Name: "wlan1", 48 | Status: 16, 49 | QualityLink: 9, 50 | QualityLevel: 8, 51 | QualityNoise: 7, 52 | DiscardedNwid: 6, 53 | DiscardedCrypt: 5, 54 | DiscardedFrag: 4, 55 | DiscardedRetry: 3, 56 | DiscardedMisc: 2, 57 | MissedBeacon: 1, 58 | }, 59 | } 60 | 61 | if len(got) != len(expected) { 62 | t.Fatalf("unexpected number of interfaces parsed %d, expected %d", len(got), len(expected)) 63 | } 64 | 65 | for i, iface := range got { 66 | if !reflect.DeepEqual(iface, expected[i]) { 67 | t.Errorf("unexpected interface got %+v, expected %+v", iface, expected[i]) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /net_xfrm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Prometheus Team 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | func TestXfrmStats(t *testing.T) { 21 | xfrmStats, err := getProcFixtures(t).NewXfrmStat() 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | for _, test := range []struct { 27 | name string 28 | want int 29 | got int 30 | }{ 31 | {name: "XfrmInError", want: 1, got: xfrmStats.XfrmInError}, 32 | {name: "XfrmInBufferError", want: 2, got: xfrmStats.XfrmInBufferError}, 33 | {name: "XfrmInHdrError", want: 4, got: xfrmStats.XfrmInHdrError}, 34 | {name: "XfrmInNoStates", want: 3, got: xfrmStats.XfrmInNoStates}, 35 | {name: "XfrmInStateProtoError", want: 40, got: xfrmStats.XfrmInStateProtoError}, 36 | {name: "XfrmInStateModeError", want: 100, got: xfrmStats.XfrmInStateModeError}, 37 | {name: "XfrmInStateSeqError", want: 6000, got: xfrmStats.XfrmInStateSeqError}, 38 | {name: "XfrmInStateExpired", want: 4, got: xfrmStats.XfrmInStateExpired}, 39 | {name: "XfrmInStateMismatch", want: 23451, got: xfrmStats.XfrmInStateMismatch}, 40 | {name: "XfrmInStateInvalid", want: 55555, got: xfrmStats.XfrmInStateInvalid}, 41 | {name: "XfrmInTmplMismatch", want: 51, got: xfrmStats.XfrmInTmplMismatch}, 42 | {name: "XfrmInNoPols", want: 65432, got: xfrmStats.XfrmInNoPols}, 43 | {name: "XfrmInPolBlock", want: 100, got: xfrmStats.XfrmInPolBlock}, 44 | {name: "XfrmInPolError", want: 10000, got: xfrmStats.XfrmInPolError}, 45 | {name: "XfrmOutError", want: 1000000, got: xfrmStats.XfrmOutError}, 46 | {name: "XfrmOutBundleGenError", want: 43321, got: xfrmStats.XfrmOutBundleGenError}, 47 | {name: "XfrmOutBundleCheckError", want: 555, got: xfrmStats.XfrmOutBundleCheckError}, 48 | {name: "XfrmOutNoStates", want: 869, got: xfrmStats.XfrmOutNoStates}, 49 | {name: "XfrmOutStateProtoError", want: 4542, got: xfrmStats.XfrmOutStateProtoError}, 50 | {name: "XfrmOutStateModeError", want: 4, got: xfrmStats.XfrmOutStateModeError}, 51 | {name: "XfrmOutStateSeqError", want: 543, got: xfrmStats.XfrmOutStateSeqError}, 52 | {name: "XfrmOutStateExpired", want: 565, got: xfrmStats.XfrmOutStateExpired}, 53 | {name: "XfrmOutPolBlock", want: 43456, got: xfrmStats.XfrmOutPolBlock}, 54 | {name: "XfrmOutPolDead", want: 7656, got: xfrmStats.XfrmOutPolDead}, 55 | {name: "XfrmOutPolError", want: 1454, got: xfrmStats.XfrmOutPolError}, 56 | {name: "XfrmFwdHdrError", want: 6654, got: xfrmStats.XfrmFwdHdrError}, 57 | {name: "XfrmOutStateInvaliad", want: 28765, got: xfrmStats.XfrmOutStateInvalid}, 58 | {name: "XfrmAcquireError", want: 24532, got: xfrmStats.XfrmAcquireError}, 59 | {name: "XfrmInStateInvalid", want: 55555, got: xfrmStats.XfrmInStateInvalid}, 60 | {name: "XfrmOutError", want: 1000000, got: xfrmStats.XfrmOutError}, 61 | } { 62 | if test.want != test.got { 63 | t.Errorf("Want %s %d, have %d", test.name, test.want, test.got) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /netstat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bufio" 18 | "os" 19 | "path/filepath" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | // NetStat contains statistics for all the counters from one file. 25 | type NetStat struct { 26 | Stats map[string][]uint64 27 | Filename string 28 | } 29 | 30 | // NetStat retrieves stats from `/proc/net/stat/`. 31 | func (fs FS) NetStat() ([]NetStat, error) { 32 | statFiles, err := filepath.Glob(fs.proc.Path("net/stat/*")) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | var netStatsTotal []NetStat 38 | 39 | for _, filePath := range statFiles { 40 | procNetstat, err := parseNetstat(filePath) 41 | if err != nil { 42 | return nil, err 43 | } 44 | procNetstat.Filename = filepath.Base(filePath) 45 | 46 | netStatsTotal = append(netStatsTotal, procNetstat) 47 | } 48 | return netStatsTotal, nil 49 | } 50 | 51 | // parseNetstat parses the metrics from `/proc/net/stat/` file 52 | // and returns a NetStat structure. 53 | func parseNetstat(filePath string) (NetStat, error) { 54 | netStat := NetStat{ 55 | Stats: make(map[string][]uint64), 56 | } 57 | file, err := os.Open(filePath) 58 | if err != nil { 59 | return netStat, err 60 | } 61 | defer file.Close() 62 | 63 | scanner := bufio.NewScanner(file) 64 | scanner.Scan() 65 | 66 | // First string is always a header for stats 67 | var headers []string 68 | headers = append(headers, strings.Fields(scanner.Text())...) 69 | 70 | // Other strings represent per-CPU counters 71 | for scanner.Scan() { 72 | for num, counter := range strings.Fields(scanner.Text()) { 73 | value, err := strconv.ParseUint(counter, 16, 64) 74 | if err != nil { 75 | return NetStat{}, err 76 | } 77 | netStat.Stats[headers[num]] = append(netStat.Stats[headers[num]], value) 78 | } 79 | } 80 | 81 | return netStat, nil 82 | } 83 | -------------------------------------------------------------------------------- /nfs/parse_nfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package nfs 15 | 16 | import ( 17 | "bufio" 18 | "fmt" 19 | "io" 20 | "strings" 21 | 22 | "github.com/prometheus/procfs/internal/util" 23 | ) 24 | 25 | // ParseClientRPCStats returns stats read from /proc/net/rpc/nfs. 26 | func ParseClientRPCStats(r io.Reader) (*ClientRPCStats, error) { 27 | stats := &ClientRPCStats{} 28 | 29 | scanner := bufio.NewScanner(r) 30 | for scanner.Scan() { 31 | line := scanner.Text() 32 | parts := strings.Fields(scanner.Text()) 33 | // require at least 34 | if len(parts) < 2 { 35 | return nil, fmt.Errorf("invalid NFS metric line %q", line) 36 | } 37 | 38 | values, err := util.ParseUint64s(parts[1:]) 39 | if err != nil { 40 | return nil, fmt.Errorf("error parsing NFS metric line: %w", err) 41 | } 42 | 43 | switch metricLine := parts[0]; metricLine { 44 | case "net": 45 | stats.Network, err = parseNetwork(values) 46 | case "rpc": 47 | stats.ClientRPC, err = parseClientRPC(values) 48 | case "proc2": 49 | stats.V2Stats, err = parseV2Stats(values) 50 | case "proc3": 51 | stats.V3Stats, err = parseV3Stats(values) 52 | case "proc4": 53 | stats.ClientV4Stats, err = parseClientV4Stats(values) 54 | default: 55 | return nil, fmt.Errorf("unknown NFS metric line %q", metricLine) 56 | } 57 | if err != nil { 58 | return nil, fmt.Errorf("errors parsing NFS metric line: %w", err) 59 | } 60 | } 61 | 62 | if err := scanner.Err(); err != nil { 63 | return nil, fmt.Errorf("error scanning NFS file: %w", err) 64 | } 65 | 66 | return stats, nil 67 | } 68 | -------------------------------------------------------------------------------- /nfs/parse_nfsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package nfs 15 | 16 | import ( 17 | "bufio" 18 | "fmt" 19 | "io" 20 | "strings" 21 | 22 | "github.com/prometheus/procfs/internal/util" 23 | ) 24 | 25 | // ParseServerRPCStats returns stats read from /proc/net/rpc/nfsd. 26 | func ParseServerRPCStats(r io.Reader) (*ServerRPCStats, error) { 27 | stats := &ServerRPCStats{} 28 | 29 | scanner := bufio.NewScanner(r) 30 | for scanner.Scan() { 31 | line := scanner.Text() 32 | parts := strings.Fields(scanner.Text()) 33 | // require at least 34 | if len(parts) < 2 { 35 | return nil, fmt.Errorf("invalid NFSd metric line %q", line) 36 | } 37 | label := parts[0] 38 | 39 | var values []uint64 40 | var err error 41 | if label == "th" { 42 | if len(parts) < 3 { 43 | return nil, fmt.Errorf("invalid NFSd th metric line %q", line) 44 | } 45 | values, err = util.ParseUint64s(parts[1:3]) 46 | } else { 47 | values, err = util.ParseUint64s(parts[1:]) 48 | } 49 | if err != nil { 50 | return nil, fmt.Errorf("error parsing NFSd metric line: %w", err) 51 | } 52 | 53 | switch metricLine := parts[0]; metricLine { 54 | case "rc": 55 | stats.ReplyCache, err = parseReplyCache(values) 56 | case "fh": 57 | stats.FileHandles, err = parseFileHandles(values) 58 | case "io": 59 | stats.InputOutput, err = parseInputOutput(values) 60 | case "th": 61 | stats.Threads, err = parseThreads(values) 62 | case "ra": 63 | stats.ReadAheadCache, err = parseReadAheadCache(values) 64 | case "net": 65 | stats.Network, err = parseNetwork(values) 66 | case "rpc": 67 | stats.ServerRPC, err = parseServerRPC(values) 68 | case "proc2": 69 | stats.V2Stats, err = parseV2Stats(values) 70 | case "proc3": 71 | stats.V3Stats, err = parseV3Stats(values) 72 | case "proc4": 73 | stats.ServerV4Stats, err = parseServerV4Stats(values) 74 | case "proc4ops": 75 | stats.V4Ops, err = parseV4Ops(values) 76 | case "wdeleg_getattr": 77 | stats.WdelegGetattr = values[0] 78 | default: 79 | return nil, fmt.Errorf("unknown NFSd metric line %q", metricLine) 80 | } 81 | if err != nil { 82 | return nil, fmt.Errorf("errors parsing NFSd metric line: %w", err) 83 | } 84 | } 85 | 86 | if err := scanner.Err(); err != nil { 87 | return nil, fmt.Errorf("error scanning NFSd file: %w", err) 88 | } 89 | 90 | return stats, nil 91 | } 92 | -------------------------------------------------------------------------------- /proc_cgroup_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "reflect" 18 | "testing" 19 | ) 20 | 21 | func TestParseCgroupString(t *testing.T) { 22 | tests := []struct { 23 | name string 24 | s string 25 | shouldErr bool 26 | cgroup *Cgroup 27 | }{ 28 | { 29 | name: "cgroups-v1 simple line", 30 | s: "10:rdma:/", 31 | shouldErr: false, 32 | cgroup: &Cgroup{ 33 | HierarchyID: 10, 34 | Controllers: []string{"rdma"}, 35 | Path: "/", 36 | }, 37 | }, 38 | { 39 | name: "cgroups-v1 multi-hier line", 40 | s: "3:cpu,cpuacct:/user.slice/user-1000.slice/session-10.scope", 41 | shouldErr: false, 42 | cgroup: &Cgroup{ 43 | HierarchyID: 3, 44 | Controllers: []string{"cpu", "cpuacct"}, 45 | Path: "/user.slice/user-1000.slice/session-10.scope", 46 | }, 47 | }, 48 | { 49 | name: "cgroup-v2 line", 50 | s: "0::/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service", 51 | shouldErr: false, 52 | cgroup: &Cgroup{ 53 | HierarchyID: 0, 54 | Controllers: nil, 55 | Path: "/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service", 56 | }, 57 | }, 58 | { 59 | name: "path containing colons", 60 | s: "0::/some/long/path:foobar", 61 | shouldErr: false, 62 | cgroup: &Cgroup{ 63 | HierarchyID: 0, 64 | Controllers: nil, 65 | Path: "/some/long/path:foobar", 66 | }, 67 | }, 68 | { 69 | name: "bad hierarchy ID field", 70 | s: "a:cpu:/", 71 | shouldErr: true, 72 | cgroup: nil, 73 | }, 74 | } 75 | 76 | for i, test := range tests { 77 | t.Logf("[%02d] test %q", i, test.name) 78 | 79 | cgroup, err := parseCgroupString(test.s) 80 | 81 | if test.shouldErr && err == nil { 82 | t.Errorf("%s: expected an error, but none occurred", test.name) 83 | } 84 | if !test.shouldErr && err != nil { 85 | t.Errorf("%s: unexpected error: %v", test.name, err) 86 | } 87 | 88 | if want, have := test.cgroup, cgroup; !reflect.DeepEqual(want, have) { 89 | t.Errorf("cgroup:\nwant:\n%+v\nhave:\n%+v", want, have) 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /proc_cgroups_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "reflect" 18 | "testing" 19 | ) 20 | 21 | func TestParseCgroupSummaryString(t *testing.T) { 22 | tests := []struct { 23 | name string 24 | s string 25 | shouldErr bool 26 | CgroupSummary *CgroupSummary 27 | }{ 28 | { 29 | name: "cpuset simple line", 30 | s: "cpuset 7 148 1", 31 | shouldErr: false, 32 | CgroupSummary: &CgroupSummary{ 33 | SubsysName: "cpuset", 34 | Hierarchy: 7, 35 | Cgroups: 148, 36 | Enabled: 1, 37 | }, 38 | }, 39 | { 40 | name: "memory cgroup number mis format", 41 | s: "memory 9 ## 1", 42 | shouldErr: true, 43 | CgroupSummary: nil, 44 | }, 45 | } 46 | 47 | for i, test := range tests { 48 | t.Logf("[%02d] test %q", i, test.name) 49 | 50 | CgroupSummary, err := parseCgroupSummaryString(test.s) 51 | 52 | if test.shouldErr && err == nil { 53 | t.Errorf("%s: expected an error, but none occurred", test.name) 54 | } 55 | if !test.shouldErr && err != nil { 56 | t.Errorf("%s: unexpected error: %v", test.name, err) 57 | } 58 | 59 | if want, have := test.CgroupSummary, CgroupSummary; !reflect.DeepEqual(want, have) { 60 | t.Errorf("cgroup:\nwant:\n%+v\nhave:\n%+v", want, have) 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /proc_environ.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "strings" 18 | 19 | "github.com/prometheus/procfs/internal/util" 20 | ) 21 | 22 | // Environ reads process environments from `/proc//environ`. 23 | func (p Proc) Environ() ([]string, error) { 24 | environments := make([]string, 0) 25 | 26 | data, err := util.ReadFileNoStat(p.path("environ")) 27 | if err != nil { 28 | return environments, err 29 | } 30 | 31 | environments = strings.Split(string(data), "\000") 32 | if len(environments) > 0 { 33 | environments = environments[:len(environments)-1] 34 | } 35 | 36 | return environments, nil 37 | } 38 | -------------------------------------------------------------------------------- /proc_environ_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestProcEnviron(t *testing.T) { 19 | p, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | environments, err := p.Environ() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | expectedEnvironments := []string{ 30 | "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 31 | "HOSTNAME=cd24e11f73a5", 32 | "TERM=xterm", 33 | "GOLANG_VERSION=1.12.5", 34 | "GOPATH=/go", 35 | "HOME=/root", 36 | } 37 | 38 | if want, have := len(expectedEnvironments), len(environments); want != have { 39 | t.Errorf("want %d parsed environments, have %d", want, have) 40 | } 41 | 42 | for i, environment := range environments { 43 | if want, have := expectedEnvironments[i], environment; want != have { 44 | t.Errorf("%d: want %v, have %v", i, want, have) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /proc_fdinfo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestInotifyWatchLen(t *testing.T) { 19 | p1, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | fdinfos, err := p1.FileDescriptorsInfo() 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | l, err := fdinfos.InotifyWatchLen() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | if want, have := 3, l; want != have { 32 | t.Errorf("want length %d, have %d", want, have) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /proc_interrupts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bufio" 18 | "bytes" 19 | "errors" 20 | "fmt" 21 | "io" 22 | "strconv" 23 | "strings" 24 | 25 | "github.com/prometheus/procfs/internal/util" 26 | ) 27 | 28 | // Interrupt represents a single interrupt line. 29 | type Interrupt struct { 30 | // Info is the type of interrupt. 31 | Info string 32 | // Devices is the name of the device that is located at that IRQ 33 | Devices string 34 | // Values is the number of interrupts per CPU. 35 | Values []string 36 | } 37 | 38 | // Interrupts models the content of /proc/interrupts. Key is the IRQ number. 39 | // - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-interrupts 40 | // - https://raspberrypi.stackexchange.com/questions/105802/explanation-of-proc-interrupts-output 41 | type Interrupts map[string]Interrupt 42 | 43 | // Interrupts creates a new instance from a given Proc instance. 44 | func (p Proc) Interrupts() (Interrupts, error) { 45 | data, err := util.ReadFileNoStat(p.path("interrupts")) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return parseInterrupts(bytes.NewReader(data)) 50 | } 51 | 52 | func parseInterrupts(r io.Reader) (Interrupts, error) { 53 | var ( 54 | interrupts = Interrupts{} 55 | scanner = bufio.NewScanner(r) 56 | ) 57 | 58 | if !scanner.Scan() { 59 | return nil, errors.New("interrupts empty") 60 | } 61 | cpuNum := len(strings.Fields(scanner.Text())) // one header per cpu 62 | 63 | for scanner.Scan() { 64 | parts := strings.Fields(scanner.Text()) 65 | if len(parts) == 0 { // skip empty lines 66 | continue 67 | } 68 | if len(parts) < 2 { 69 | return nil, fmt.Errorf("%w: Not enough fields in interrupts (expected 2+ fields but got %d): %s", ErrFileParse, len(parts), parts) 70 | } 71 | intName := parts[0][:len(parts[0])-1] // remove trailing : 72 | 73 | if len(parts) == 2 { 74 | interrupts[intName] = Interrupt{ 75 | Info: "", 76 | Devices: "", 77 | Values: []string{ 78 | parts[1], 79 | }, 80 | } 81 | continue 82 | } 83 | 84 | intr := Interrupt{ 85 | Values: parts[1 : cpuNum+1], 86 | } 87 | 88 | if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt 89 | intr.Info = parts[cpuNum+1] 90 | intr.Devices = strings.Join(parts[cpuNum+2:], " ") 91 | } else { 92 | intr.Info = strings.Join(parts[cpuNum+1:], " ") 93 | } 94 | interrupts[intName] = intr 95 | } 96 | 97 | return interrupts, scanner.Err() 98 | } 99 | -------------------------------------------------------------------------------- /proc_interrupts_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "reflect" 18 | "testing" 19 | ) 20 | 21 | func TestProcInterrupts(t *testing.T) { 22 | p, err := getProcFixtures(t).Proc(26231) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | interrupts, err := p.Interrupts() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | if want, have := 47, len(interrupts); want != have { 33 | t.Errorf("want length %d, have %d", want, have) 34 | } 35 | 36 | for _, test := range []struct { 37 | name string 38 | irq string 39 | want Interrupt 40 | }{ 41 | { 42 | name: "first line", 43 | irq: "0", 44 | want: Interrupt{ 45 | Info: "IO-APIC", 46 | Devices: "2-edge timer", 47 | Values: []string{"49", "0", "0", "0"}, 48 | }, 49 | }, 50 | { 51 | name: "last line", 52 | irq: "PIW", 53 | want: Interrupt{ 54 | Info: "Posted-interrupt wakeup event", 55 | Devices: "", 56 | Values: []string{"0", "0", "0", "0"}, 57 | }, 58 | }, 59 | { 60 | name: "empty devices", 61 | irq: "LOC", 62 | want: Interrupt{ 63 | Info: "Local timer interrupts", 64 | Devices: "", 65 | Values: []string{"10196", "7429", "8542", "8229"}, 66 | }, 67 | }, 68 | { 69 | name: "single value", 70 | irq: "ERR", 71 | want: Interrupt{ 72 | Info: "", 73 | Devices: "", 74 | Values: []string{"0"}, 75 | }, 76 | }, 77 | } { 78 | t.Run(test.name, func(t *testing.T) { 79 | if value, ok := interrupts[test.irq]; ok { 80 | if value.Info != test.want.Info { 81 | t.Errorf("info: want %s, have %s", test.want.Info, value.Info) 82 | } 83 | if value.Devices != test.want.Devices { 84 | t.Errorf("devices: want %s, have %s", test.want.Devices, value.Devices) 85 | } 86 | if !reflect.DeepEqual(value.Values, test.want.Values) { 87 | t.Errorf("values: want %v, have %v", test.want.Values, value.Values) 88 | } 89 | } else { 90 | t.Errorf("IRQ %s not found", test.irq) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /proc_io.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | 19 | "github.com/prometheus/procfs/internal/util" 20 | ) 21 | 22 | // ProcIO models the content of /proc//io. 23 | type ProcIO struct { 24 | // Chars read. 25 | RChar uint64 26 | // Chars written. 27 | WChar uint64 28 | // Read syscalls. 29 | SyscR uint64 30 | // Write syscalls. 31 | SyscW uint64 32 | // Bytes read. 33 | ReadBytes uint64 34 | // Bytes written. 35 | WriteBytes uint64 36 | // Bytes written, but taking into account truncation. See 37 | // Documentation/filesystems/proc.txt in the kernel sources for 38 | // detailed explanation. 39 | CancelledWriteBytes int64 40 | } 41 | 42 | // IO creates a new ProcIO instance from a given Proc instance. 43 | func (p Proc) IO() (ProcIO, error) { 44 | pio := ProcIO{} 45 | 46 | data, err := util.ReadFileNoStat(p.path("io")) 47 | if err != nil { 48 | return pio, err 49 | } 50 | 51 | ioFormat := "rchar: %d\nwchar: %d\nsyscr: %d\nsyscw: %d\n" + 52 | "read_bytes: %d\nwrite_bytes: %d\n" + 53 | "cancelled_write_bytes: %d\n" //nolint:misspell 54 | 55 | _, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR, 56 | &pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes) 57 | 58 | return pio, err 59 | } 60 | -------------------------------------------------------------------------------- /proc_io_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestProcIO(t *testing.T) { 19 | p, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | s, err := p.IO() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | for _, test := range []struct { 30 | name string 31 | want int64 32 | have int64 33 | }{ 34 | {name: "RChar", want: 750339, have: int64(s.RChar)}, 35 | {name: "WChar", want: 818609, have: int64(s.WChar)}, 36 | {name: "SyscR", want: 7405, have: int64(s.SyscR)}, 37 | {name: "SyscW", want: 5245, have: int64(s.SyscW)}, 38 | {name: "ReadBytes", want: 1024, have: int64(s.ReadBytes)}, 39 | {name: "WriteBytes", want: 2048, have: int64(s.WriteBytes)}, 40 | {name: "CancelledWriteBytes", want: -1024, have: s.CancelledWriteBytes}, 41 | } { 42 | if test.want != test.have { 43 | t.Errorf("want %s %d, have %d", test.name, test.want, test.have) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /proc_limits_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestLimits(t *testing.T) { 19 | p, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | l, err := p.Limits() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | for _, test := range []struct { 30 | name string 31 | want uint64 32 | have uint64 33 | }{ 34 | {name: "cpu time", want: 18446744073709551615, have: l.CPUTime}, 35 | {name: "open files", want: 2048, have: l.OpenFiles}, 36 | {name: "msgqueue size", want: 819200, have: l.MsqqueueSize}, 37 | {name: "nice priority", want: 0, have: l.NicePriority}, 38 | {name: "address space", want: 8589934592, have: l.AddressSpace}, 39 | } { 40 | if test.want != test.have { 41 | t.Errorf("want %s %d, have %d", test.name, test.want, test.have) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /proc_maps32_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && (386 || arm || mips || mipsle) 15 | // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 16 | // +build 386 arm mips mipsle 17 | 18 | package procfs 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/google/go-cmp/cmp" 24 | "golang.org/x/sys/unix" 25 | ) 26 | 27 | func TestProcMaps(t *testing.T) { 28 | tsts32 := []*ProcMap{ 29 | { 30 | StartAddr: 0x08048000, 31 | EndAddr: 0x08089000, 32 | Perms: &ProcMapPermissions{true, false, true, false, true}, 33 | Offset: 0, 34 | Dev: unix.Mkdev(0x03, 0x01), 35 | Inode: 104219, 36 | Pathname: "/bin/tcsh", 37 | }, 38 | { 39 | StartAddr: 0x08089000, 40 | EndAddr: 0x0808c000, 41 | Perms: &ProcMapPermissions{true, true, false, false, true}, 42 | Offset: 266240, 43 | Dev: unix.Mkdev(0x03, 0x01), 44 | Inode: 104219, 45 | Pathname: "/bin/tcsh", 46 | }, 47 | { 48 | StartAddr: 0x0808c000, 49 | EndAddr: 0x08146000, 50 | Perms: &ProcMapPermissions{true, true, true, false, true}, 51 | Offset: 0, 52 | Dev: unix.Mkdev(0x00, 0x00), 53 | Inode: 0, 54 | Pathname: "", 55 | }, 56 | { 57 | StartAddr: 0x40000000, 58 | EndAddr: 0x40015000, 59 | Perms: &ProcMapPermissions{true, false, true, false, true}, 60 | Offset: 0, 61 | Dev: unix.Mkdev(0x03, 0x01), 62 | Inode: 61874, 63 | Pathname: "/lib/ld-2.3.2.so", 64 | }, 65 | } 66 | 67 | // 32-bit test pid and fixtures 68 | tpid := 26234 69 | tsts := tsts32 70 | 71 | p, err := getProcFixtures(t).Proc(tpid) 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | 76 | maps, err := p.ProcMaps() 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | if want, have := len(maps), len(tsts); want > have { 82 | t.Errorf("want at least %d parsed proc/map entries, have %d", want, have) 83 | } 84 | 85 | for idx, tst := range tsts { 86 | want, got := tst, maps[idx] 87 | if diff := cmp.Diff(want, got); diff != "" { 88 | t.Fatalf("unexpected proc/map entry (-want +got):\n%s", diff) 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /proc_netstat_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | func TestProcNetstat(t *testing.T) { 21 | p, err := getProcFixtures(t).Proc(26231) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | procNetstat, err := p.Netstat() 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | for _, test := range []struct { 32 | name string 33 | want float64 34 | have float64 35 | }{ 36 | {name: "pid", want: 26231, have: float64(procNetstat.PID)}, 37 | {name: "TcpExt:SyncookiesSent", want: 0, have: *procNetstat.SyncookiesSent}, 38 | {name: "TcpExt:EmbryonicRsts", want: 1, have: *procNetstat.EmbryonicRsts}, 39 | {name: "TcpExt:TW", want: 83, have: *procNetstat.TW}, 40 | {name: "TcpExt:PAWSEstab", want: 3640, have: *procNetstat.PAWSEstab}, 41 | 42 | {name: "IpExt:InNoRoutes", want: 0, have: *procNetstat.InNoRoutes}, 43 | {name: "IpExt:InMcastPkts", want: 208, have: *procNetstat.InMcastPkts}, 44 | {name: "IpExt:OutMcastPkts", want: 214, have: *procNetstat.OutMcastPkts}, 45 | } { 46 | if test.want != test.have { 47 | t.Errorf("want %s %f, have %f", test.name, test.want, test.have) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /proc_ns.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "strconv" 20 | "strings" 21 | ) 22 | 23 | // Namespace represents a single namespace of a process. 24 | type Namespace struct { 25 | Type string // Namespace type. 26 | Inode uint32 // Inode number of the namespace. If two processes are in the same namespace their inodes will match. 27 | } 28 | 29 | // Namespaces contains all of the namespaces that the process is contained in. 30 | type Namespaces map[string]Namespace 31 | 32 | // Namespaces reads from /proc//ns/* to get the namespaces of which the 33 | // process is a member. 34 | func (p Proc) Namespaces() (Namespaces, error) { 35 | d, err := os.Open(p.path("ns")) 36 | if err != nil { 37 | return nil, err 38 | } 39 | defer d.Close() 40 | 41 | names, err := d.Readdirnames(-1) 42 | if err != nil { 43 | return nil, fmt.Errorf("%w: failed to read contents of ns dir: %w", ErrFileRead, err) 44 | } 45 | 46 | ns := make(Namespaces, len(names)) 47 | for _, name := range names { 48 | target, err := os.Readlink(p.path("ns", name)) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | fields := strings.SplitN(target, ":", 2) 54 | if len(fields) != 2 { 55 | return nil, fmt.Errorf("%w: namespace type and inode from %q", ErrFileParse, target) 56 | } 57 | 58 | typ := fields[0] 59 | inode, err := strconv.ParseUint(strings.Trim(fields[1], "[]"), 10, 32) 60 | if err != nil { 61 | return nil, fmt.Errorf("%w: inode from %q: %w", ErrFileParse, fields[1], err) 62 | } 63 | 64 | ns[name] = Namespace{typ, uint32(inode)} 65 | } 66 | 67 | return ns, nil 68 | } 69 | -------------------------------------------------------------------------------- /proc_ns_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | func TestNewNamespaces(t *testing.T) { 21 | p, err := getProcFixtures(t).Proc(26231) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | namespaces, err := p.Namespaces() 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | expectedNamespaces := map[string]Namespace{ 32 | "mnt": {"mnt", 4026531840}, 33 | "net": {"net", 4026531993}, 34 | } 35 | 36 | if want, have := len(expectedNamespaces), len(namespaces); want != have { 37 | t.Errorf("want %d parsed namespaces, have %d", want, have) 38 | } 39 | for _, ns := range namespaces { 40 | if want, have := expectedNamespaces[ns.Type], ns; want != have { 41 | t.Errorf("%s: want %v, have %v", ns.Type, want, have) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /proc_smaps_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build !windows 15 | // +build !windows 16 | 17 | package procfs 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func BenchmarkProcSMapsRollup(b *testing.B) { 24 | fs, err := NewFS(procTestFixtures) 25 | if err != nil { 26 | b.Fatalf("Creating pseudo fs from getProcFixtures failed at fixtures/proc with error: %s", err) 27 | } 28 | 29 | p, err := fs.Proc(26231) 30 | if err != nil { 31 | b.Fatal(err) 32 | } 33 | 34 | b.ResetTimer() 35 | for n := 0; n < b.N; n++ { 36 | _, _ = p.ProcSMapsRollup() 37 | } 38 | } 39 | 40 | func TestProcSmapsRollup(t *testing.T) { 41 | p, err := getProcFixtures(t).Proc(26231) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | s1, err := p.ProcSMapsRollup() 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | s2, err := p.procSMapsRollupManual() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | 56 | cases := []struct { 57 | name string 58 | smaps ProcSMapsRollup 59 | }{ 60 | { 61 | name: "ProcSMapsRollup", 62 | smaps: s1, 63 | }, 64 | { 65 | name: "procSMapsRollupManual", 66 | smaps: s2, 67 | }, 68 | } 69 | 70 | for _, c := range cases { 71 | for _, test := range []struct { 72 | name string 73 | want uint64 74 | have uint64 75 | }{ 76 | {name: "Rss", want: 29948 * 1024, have: c.smaps.Rss}, 77 | {name: "Pss", want: 29944 * 1024, have: c.smaps.Pss}, 78 | {name: "SharedClean", want: 4 * 1024, have: c.smaps.SharedClean}, 79 | {name: "SharedDirty", want: 0 * 1024, have: c.smaps.SharedDirty}, 80 | {name: "PrivateClean", want: 15548 * 1024, have: c.smaps.PrivateClean}, 81 | {name: "PrivateDirty", want: 14396 * 1024, have: c.smaps.PrivateDirty}, 82 | {name: "Referenced", want: 24752 * 1024, have: c.smaps.Referenced}, 83 | {name: "Anonymous", want: 20756 * 1024, have: c.smaps.Anonymous}, 84 | {name: "Swap", want: 1940 * 1024, have: c.smaps.Swap}, 85 | {name: "SwapPss", want: 1940 * 1024, have: c.smaps.SwapPss}, 86 | } { 87 | if test.want != test.have { 88 | t.Errorf("want %s %s %d, have %d", c.name, test.name, test.want, test.have) 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /proc_snmp6_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestProcSnmp6(t *testing.T) { 19 | p, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | procSnmp6, err := p.Snmp6() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | for _, test := range []struct { 30 | name string 31 | want float64 32 | have float64 33 | }{ 34 | {name: "pid", want: 26231, have: float64(procSnmp6.PID)}, 35 | {name: "Ip6InReceives", want: 92166, have: *procSnmp6.InReceives}, 36 | {name: "Ip6InDelivers", want: 92053, have: *procSnmp6.InDelivers}, 37 | {name: "Ip6OutNoRoutes", want: 169, have: *procSnmp6.OutNoRoutes}, 38 | {name: "Ip6InOctets", want: 113479132, have: *procSnmp6.InOctets}, 39 | {name: "Icmp6InMsgs", want: 142, have: *procSnmp6.InMsgs}, 40 | {name: "Udp6InDatagrams", want: 2016, have: *procSnmp6.Udp6.InDatagrams}, 41 | {name: "UdpLite6InDatagrams", want: 0, have: *procSnmp6.UdpLite6.InDatagrams}, 42 | } { 43 | if test.want != test.have { 44 | t.Errorf("want %s %f, have %f", test.name, test.want, test.have) 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /proc_snmp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestProcSnmp(t *testing.T) { 19 | p, err := getProcFixtures(t).Proc(26231) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | procSnmp, err := p.Snmp() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | for _, test := range []struct { 30 | name string 31 | want float64 32 | have float64 33 | }{ 34 | {name: "pid", want: 26231, have: float64(procSnmp.PID)}, 35 | {name: "IP:Forwarding", want: 2, have: *procSnmp.Forwarding}, 36 | {name: "IP:DefaultTTL", want: 64, have: *procSnmp.DefaultTTL}, 37 | {name: "Icmp:InMsgs", want: 45, have: *procSnmp.InMsgs}, 38 | {name: "IcmpMsg:InType3", want: 45, have: *procSnmp.InType3}, 39 | {name: "IcmpMsg:OutType3", want: 50, have: *procSnmp.OutType3}, 40 | {name: "TCP:RtoAlgorithm", want: 1, have: *procSnmp.RtoAlgorithm}, 41 | {name: "TCP:RtoMin", want: 200, have: *procSnmp.RtoMin}, 42 | {name: "Udp:InDatagrams", want: 10179, have: *procSnmp.Udp.InDatagrams}, 43 | {name: "Udp:NoPorts", want: 50, have: *procSnmp.Udp.NoPorts}, 44 | {name: "UdpLite:InDatagrams", want: 0, have: *procSnmp.UdpLite.NoPorts}, 45 | {name: "UdpLite:NoPorts", want: 0, have: *procSnmp.UdpLite.NoPorts}, 46 | } { 47 | if test.want != test.have { 48 | t.Errorf("want %s %f, have %f", test.name, test.want, test.have) 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /proc_sys.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "strings" 19 | 20 | "github.com/prometheus/procfs/internal/util" 21 | ) 22 | 23 | func sysctlToPath(sysctl string) string { 24 | return strings.ReplaceAll(sysctl, ".", "/") 25 | } 26 | 27 | func (fs FS) SysctlStrings(sysctl string) ([]string, error) { 28 | value, err := util.SysReadFile(fs.proc.Path("sys", sysctlToPath(sysctl))) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return strings.Fields(value), nil 33 | 34 | } 35 | 36 | func (fs FS) SysctlInts(sysctl string) ([]int, error) { 37 | fields, err := fs.SysctlStrings(sysctl) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | values := make([]int, len(fields)) 43 | for i, f := range fields { 44 | vp := util.NewValueParser(f) 45 | values[i] = vp.Int() 46 | if err := vp.Err(); err != nil { 47 | return nil, fmt.Errorf("%w: field %d in sysctl %s is not a valid int: %w", ErrFileParse, i, sysctl, err) 48 | } 49 | } 50 | return values, nil 51 | } 52 | -------------------------------------------------------------------------------- /proc_sys_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/google/go-cmp/cmp" 20 | ) 21 | 22 | func TestSysctlInts(t *testing.T) { 23 | fs := getProcFixtures(t) 24 | 25 | for _, tc := range []struct { 26 | sysctl string 27 | want []int 28 | }{ 29 | {"kernel.random.entropy_avail", []int{3943}}, 30 | {"vm.lowmem_reserve_ratio", []int{256, 256, 32, 0, 0}}, 31 | } { 32 | t.Run(tc.sysctl, func(t *testing.T) { 33 | got, err := fs.SysctlInts(tc.sysctl) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | if diff := cmp.Diff(tc.want, got); diff != "" { 38 | t.Fatalf("unexpected syscall value(-want +got):\n%s", diff) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func TestSysctlStrings(t *testing.T) { 45 | fs := getProcFixtures(t) 46 | 47 | for _, tc := range []struct { 48 | sysctl string 49 | want []string 50 | }{ 51 | {"kernel.seccomp.actions_avail", []string{"kill_process", "kill_thread", "trap", "errno", "trace", "log", "allow"}}, 52 | } { 53 | t.Run(tc.sysctl, func(t *testing.T) { 54 | got, err := fs.SysctlStrings(tc.sysctl) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | if diff := cmp.Diff(tc.want, got); diff != "" { 59 | t.Fatalf("unexpected syscall value(-want +got):\n%s", diff) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | func TestSysctlIntsError(t *testing.T) { 66 | fs := getProcFixtures(t) 67 | 68 | for _, tc := range []struct { 69 | sysctl string 70 | want string 71 | }{ 72 | {"kernel.seccomp.actions_avail", "error parsing file: field 0 in sysctl kernel.seccomp.actions_avail is not a valid int: strconv.ParseInt: parsing \"kill_process\": invalid syntax"}, 73 | } { 74 | t.Run(tc.sysctl, func(t *testing.T) { 75 | _, err := fs.SysctlInts(tc.sysctl) 76 | if err == nil { 77 | t.Fatal("Error Parsing File: expected error") 78 | } 79 | if diff := cmp.Diff(tc.want, err.Error()); diff != "" { 80 | t.Fatalf("unexpected syscall value(-want +got):\n%s", diff) 81 | } 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /scripts/check_build_tags.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | fail=0 4 | while read -r f ; do 5 | if ! grep -q '+build linux' "$f" ; then 6 | echo "missing linux build tag: $f" 7 | fail=1 8 | fi 9 | done < <(find sysfs -name '*.go') 10 | 11 | exit "${fail}" 12 | 13 | -------------------------------------------------------------------------------- /scripts/check_license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright 2018 The Prometheus Authors 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | check_license() { 17 | local file="" 18 | for file in $(find . -type f -iname '*.go' ! -path './vendor/*'); do 19 | head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" || echo " ${file}" 20 | done 21 | } 22 | 23 | licRes=$(check_license) 24 | 25 | if [ -n "${licRes}" ]; then 26 | echo "license header checking failed:" 27 | echo "${licRes}" 28 | exit 255 29 | fi 30 | -------------------------------------------------------------------------------- /selinuxfs/avc_cache_stats.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import ( 20 | "bufio" 21 | "fmt" 22 | "os" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // SELinux access vector cache statistics. 28 | type AVCStat struct { 29 | // Number of total lookups 30 | Lookups uint64 31 | // Number of total hits 32 | Hits uint64 33 | // Number of total misses 34 | Misses uint64 35 | // Number of total allocations 36 | Allocations uint64 37 | // Number of total reclaims 38 | Reclaims uint64 39 | // Number of total frees 40 | Frees uint64 41 | } 42 | 43 | // ParseAVCStats returns the total SELinux access vector cache statistics, 44 | // or error on failure. 45 | func (fs FS) ParseAVCStats() (AVCStat, error) { 46 | avcStat := AVCStat{} 47 | 48 | file, err := os.Open(fs.selinux.Path("avc/cache_stats")) 49 | if err != nil { 50 | return avcStat, err 51 | } 52 | defer file.Close() 53 | 54 | scanner := bufio.NewScanner(file) 55 | scanner.Scan() // Skip header 56 | 57 | for scanner.Scan() { 58 | avcValues := strings.Fields(scanner.Text()) 59 | 60 | if len(avcValues) != 6 { 61 | return avcStat, fmt.Errorf("invalid AVC stat line: %s", 62 | scanner.Text()) 63 | } 64 | 65 | lookups, err := strconv.ParseUint(avcValues[0], 0, 64) 66 | if err != nil { 67 | return avcStat, fmt.Errorf("could not parse expected integer value for lookups") 68 | } 69 | 70 | hits, err := strconv.ParseUint(avcValues[1], 0, 64) 71 | if err != nil { 72 | return avcStat, fmt.Errorf("could not parse expected integer value for hits") 73 | } 74 | 75 | misses, err := strconv.ParseUint(avcValues[2], 0, 64) 76 | if err != nil { 77 | return avcStat, fmt.Errorf("could not parse expected integer value for misses") 78 | } 79 | 80 | allocations, err := strconv.ParseUint(avcValues[3], 0, 64) 81 | if err != nil { 82 | return avcStat, fmt.Errorf("could not parse expected integer value for allocations") 83 | } 84 | 85 | reclaims, err := strconv.ParseUint(avcValues[4], 0, 64) 86 | if err != nil { 87 | return avcStat, fmt.Errorf("could not parse expected integer value for reclaims") 88 | } 89 | 90 | frees, err := strconv.ParseUint(avcValues[5], 0, 64) 91 | if err != nil { 92 | return avcStat, fmt.Errorf("could not parse expected integer value for frees") 93 | } 94 | 95 | avcStat.Lookups += lookups 96 | avcStat.Hits += hits 97 | avcStat.Misses += misses 98 | avcStat.Allocations += allocations 99 | avcStat.Reclaims += reclaims 100 | avcStat.Frees += frees 101 | } 102 | 103 | return avcStat, scanner.Err() 104 | } 105 | -------------------------------------------------------------------------------- /selinuxfs/avc_cache_stats_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestAVCStats(t *testing.T) { 24 | fs, err := NewFS(selinuxTestFixtures) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | avcStats, err := fs.ParseAVCStats() 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | if want, got := uint64(91590784), avcStats.Lookups; want != got { 35 | t.Errorf("want avcstat lookups %v, got %v", want, got) 36 | } 37 | 38 | if want, got := uint64(91569452), avcStats.Hits; want != got { 39 | t.Errorf("want avcstat hits %v, got %v", want, got) 40 | } 41 | 42 | if want, got := uint64(21332), avcStats.Misses; want != got { 43 | t.Errorf("want avcstat misses %v, got %v", want, got) 44 | } 45 | 46 | if want, got := uint64(21332), avcStats.Allocations; want != got { 47 | t.Errorf("want avcstat allocations %v, got %v", want, got) 48 | } 49 | 50 | if want, got := uint64(20400), avcStats.Reclaims; want != got { 51 | t.Errorf("want avcstat reclaims %v, got %v", want, got) 52 | } 53 | 54 | if want, got := uint64(20826), avcStats.Frees; want != got { 55 | t.Errorf("want avcstat frees %v, got %v", want, got) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /selinuxfs/avc_hash_stats.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import ( 20 | "bufio" 21 | "fmt" 22 | "os" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // SELinux access vector cache hashtable statistics. 28 | type AVCHashStat struct { 29 | // Number of entries 30 | Entries uint64 31 | // Number of buckets used 32 | BucketsUsed uint64 33 | // Number of buckets available 34 | BucketsAvailable uint64 35 | // Length of the longest chain 36 | LongestChain uint64 37 | } 38 | 39 | // ParseAVCHashStats returns the SELinux access vector cache hashtable 40 | // statistics, or error on failure. 41 | func (fs FS) ParseAVCHashStats() (AVCHashStat, error) { 42 | avcHashStat := AVCHashStat{} 43 | 44 | file, err := os.Open(fs.selinux.Path("avc/hash_stats")) 45 | if err != nil { 46 | return avcHashStat, err 47 | } 48 | defer file.Close() 49 | 50 | scanner := bufio.NewScanner(file) 51 | 52 | scanner.Scan() 53 | entriesValue := strings.TrimPrefix(scanner.Text(), "entries: ") 54 | 55 | scanner.Scan() 56 | bucketsValues := strings.Split(scanner.Text(), "buckets used: ") 57 | bucketsValuesTuple := strings.Split(bucketsValues[1], "/") 58 | 59 | scanner.Scan() 60 | longestChainValue := strings.TrimPrefix(scanner.Text(), "longest chain: ") 61 | 62 | avcHashStat.Entries, err = strconv.ParseUint(entriesValue, 0, 64) 63 | if err != nil { 64 | return avcHashStat, fmt.Errorf("could not parse expected integer value for hash entries") 65 | } 66 | 67 | avcHashStat.BucketsUsed, err = strconv.ParseUint(bucketsValuesTuple[0], 0, 64) 68 | if err != nil { 69 | return avcHashStat, fmt.Errorf("could not parse expected integer value for hash buckets used") 70 | } 71 | 72 | avcHashStat.BucketsAvailable, err = strconv.ParseUint(bucketsValuesTuple[1], 0, 64) 73 | if err != nil { 74 | return avcHashStat, fmt.Errorf("could not parse expected integer value for hash buckets available") 75 | } 76 | 77 | avcHashStat.LongestChain, err = strconv.ParseUint(longestChainValue, 0, 64) 78 | if err != nil { 79 | return avcHashStat, fmt.Errorf("could not parse expected integer value for hash longest chain") 80 | } 81 | 82 | return avcHashStat, scanner.Err() 83 | } 84 | -------------------------------------------------------------------------------- /selinuxfs/avc_hash_stats_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestAVCHashStat(t *testing.T) { 24 | fs, err := NewFS(selinuxTestFixtures) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | avcHashStats, err := fs.ParseAVCHashStats() 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | if want, got := uint64(503), avcHashStats.Entries; want != got { 35 | t.Errorf("want avc hash stat entries %v, got %v", want, got) 36 | } 37 | 38 | if want, got := uint64(512), avcHashStats.BucketsAvailable; want != got { 39 | t.Errorf("want avc hash stat buckets available %v, got %v", want, got) 40 | } 41 | 42 | if want, got := uint64(257), avcHashStats.BucketsUsed; want != got { 43 | t.Errorf("want avc hash stat buckets used %v, got %v", want, got) 44 | } 45 | 46 | if want, got := uint64(8), avcHashStats.LongestChain; want != got { 47 | t.Errorf("want avc hash stat longest chain %v, got %v", want, got) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /selinuxfs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import ( 20 | "github.com/prometheus/procfs/internal/fs" 21 | ) 22 | 23 | // FS represents the pseudo-filesystem selinixfs, which provides an interface to 24 | // SELinux data structures. 25 | type FS struct { 26 | selinux fs.FS 27 | } 28 | 29 | // DefaultMountPoint is the common mount point of the selinuxfs filesystem. 30 | const DefaultMountPoint = fs.DefaultSelinuxMountPoint 31 | 32 | // NewDefaultFS returns a new FS mounted under the default mountPoint. It will error 33 | // if the mount point can't be read. 34 | func NewDefaultFS() (FS, error) { 35 | return NewFS(DefaultMountPoint) 36 | } 37 | 38 | // NewFS returns a new FS mounted under the given mountPoint. It will error 39 | // if the mount point can't be read. 40 | func NewFS(mountPoint string) (FS, error) { 41 | fs, err := fs.NewFS(mountPoint) 42 | if err != nil { 43 | return FS{}, err 44 | } 45 | return FS{fs}, nil 46 | } 47 | -------------------------------------------------------------------------------- /selinuxfs/fs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux && !noselinux 15 | // +build linux,!noselinux 16 | 17 | package selinuxfs 18 | 19 | import "testing" 20 | 21 | const ( 22 | selinuxTestFixtures = "testdata/fixtures" + DefaultMountPoint 23 | ) 24 | 25 | func TestNewFS(t *testing.T) { 26 | if _, err := NewFS("foobar"); err == nil { 27 | t.Error("want NewFS to fail for non-existing mount point") 28 | } 29 | 30 | if _, err := NewFS("doc.go"); err == nil { 31 | t.Error("want NewFS to fail if mount point is not a directory") 32 | } 33 | 34 | if _, err := NewFS(selinuxTestFixtures); err != nil { 35 | t.Error("want NewFS to succeed if mount point exists") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /selinuxfs/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /slab_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | func TestSlabInfo(t *testing.T) { 21 | slabs, err := getProcFixtures(t).SlabInfo() 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | if numSlabs := len(slabs.Slabs); numSlabs != 300 { 26 | t.Errorf("expected 300 slabs, got %v", numSlabs) 27 | } 28 | if name := slabs.Slabs[0].Name; name != "pid_3" { 29 | t.Errorf("Expected slab name to be 'pid_3', got %v", name) 30 | } 31 | if objActive := slabs.Slabs[0].ObjActive; objActive != 375 { 32 | t.Errorf("Expected slab objects active to be 375, got %v", objActive) 33 | } 34 | if objNum := slabs.Slabs[0].ObjNum; objNum != 532 { 35 | t.Errorf("Expected slab objects number to be 532, got %v", objNum) 36 | } 37 | if objSize := slabs.Slabs[0].ObjSize; objSize != 576 { 38 | t.Errorf("Expected slab objects Size to be 576, got %+v", objSize) 39 | } 40 | if objPerSlab := slabs.Slabs[0].ObjPerSlab; objPerSlab != 28 { 41 | t.Errorf("Expected slab objects per slab to be 28, got %v", objPerSlab) 42 | } 43 | if pagesPerSlab := slabs.Slabs[0].PagesPerSlab; pagesPerSlab != 4 { 44 | t.Errorf("Expected pages per slab to be 4, got %v", pagesPerSlab) 45 | } 46 | if limit := slabs.Slabs[0].Limit; limit != 0 { 47 | t.Errorf("Expected limit to be 0, got %v", limit) 48 | } 49 | if batch := slabs.Slabs[0].Batch; batch != 0 { 50 | t.Errorf("Expected batch to be 0, got %v", batch) 51 | } 52 | if sharedFactor := slabs.Slabs[0].SharedFactor; sharedFactor != 0 { 53 | t.Errorf("Expected shared factor to be 0, got %v", sharedFactor) 54 | } 55 | if slabActive := slabs.Slabs[0].SlabActive; slabActive != 19 { 56 | t.Errorf("Expected slab active to be 19, got %v", slabActive) 57 | } 58 | if slabNum := slabs.Slabs[0].SlabNum; slabNum != 19 { 59 | t.Errorf("Expected slab num to be 19, got %v", slabNum) 60 | } 61 | if sharedAvail := slabs.Slabs[0].SharedAvail; sharedAvail != 0 { 62 | t.Errorf("Expected shared available to be 0, got %v", sharedAvail) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /softirqs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestSoftirqs(t *testing.T) { 19 | s, err := getProcFixtures(t).Softirqs() 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | // hi 25 | if want, have := uint64(3), s.Hi[0]; want != have { 26 | t.Errorf("want softirq HI count %d, have %d", want, have) 27 | } 28 | // timer 29 | if want, have := uint64(247490), s.Timer[1]; want != have { 30 | t.Errorf("want softirq TIMER count %d, have %d", want, have) 31 | } 32 | // net_tx 33 | if want, have := uint64(2419), s.NetTx[0]; want != have { 34 | t.Errorf("want softirq NET_TX count %d, have %d", want, have) 35 | } 36 | // net_rx 37 | if want, have := uint64(28694), s.NetRx[1]; want != have { 38 | t.Errorf("want softirq NET_RX count %d, have %d", want, have) 39 | } 40 | // block 41 | if want, have := uint64(262755), s.Block[1]; want != have { 42 | t.Errorf("want softirq BLOCK count %d, have %d", want, have) 43 | } 44 | // irq_poll 45 | if want, have := uint64(0), s.IRQPoll[0]; want != have { 46 | t.Errorf("want softirq IRQ_POLL count %d, have %d", want, have) 47 | } 48 | // tasklet 49 | if want, have := uint64(209), s.Tasklet[0]; want != have { 50 | t.Errorf("want softirq TASKLET count %d, have %d", want, have) 51 | } 52 | // sched 53 | if want, have := uint64(2278692), s.Sched[0]; want != have { 54 | t.Errorf("want softirq SCHED count %d, have %d", want, have) 55 | } 56 | // hrtimer 57 | if want, have := uint64(1281), s.HRTimer[0]; want != have { 58 | t.Errorf("want softirq HRTIMER count %d, have %d", want, have) 59 | } 60 | // rcu 61 | if want, have := uint64(532783), s.RCU[1]; want != have { 62 | t.Errorf("want softirq RCU count %d, have %d", want, have) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /stat_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import "testing" 17 | 18 | func TestStat(t *testing.T) { 19 | s, err := getProcFixtures(t).Stat() 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | // cpu 25 | if want, have := float64(301854)/userHZ, s.CPUTotal.User; want != have { 26 | t.Errorf("want cpu/user %v, have %v", want, have) 27 | } 28 | if want, have := float64(31)/userHZ, s.CPU[7].SoftIRQ; want != have { 29 | t.Errorf("want cpu7/softirq %v, have %v", want, have) 30 | } 31 | 32 | // intr 33 | if want, have := uint64(73777505), s.IRQTotal; want != have { 34 | t.Errorf("want irq/total %d, have %d", want, have) 35 | } 36 | if want, have := uint64(1), s.IRQ[8]; want != have { 37 | t.Errorf("want irq8 %d, have %d", want, have) 38 | } 39 | 40 | // ctxt 41 | if want, have := uint64(38014093), s.ContextSwitches; want != have { 42 | t.Errorf("want context switches (ctxt) %d, have %d", want, have) 43 | } 44 | 45 | // btime 46 | if want, have := uint64(1418183276), s.BootTime; want != have { 47 | t.Errorf("want boot time (btime) %d, have %d", want, have) 48 | } 49 | 50 | // processes 51 | if want, have := uint64(26442), s.ProcessCreated; want != have { 52 | t.Errorf("want process created (processes) %d, have %d", want, have) 53 | } 54 | 55 | // procs_running 56 | if want, have := uint64(2), s.ProcessesRunning; want != have { 57 | t.Errorf("want processes running (procs_running) %d, have %d", want, have) 58 | } 59 | 60 | // procs_blocked 61 | if want, have := uint64(1), s.ProcessesBlocked; want != have { 62 | t.Errorf("want processes blocked (procs_blocked) %d, have %d", want, have) 63 | } 64 | 65 | // softirq 66 | if want, have := uint64(5057579), s.SoftIRQTotal; want != have { 67 | t.Errorf("want softirq total %d, have %d", want, have) 68 | } 69 | 70 | if want, have := uint64(508444), s.SoftIRQ.Rcu; want != have { 71 | t.Errorf("want softirq RCU %d, have %d", want, have) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /swaps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "bufio" 18 | "bytes" 19 | "fmt" 20 | "strconv" 21 | "strings" 22 | 23 | "github.com/prometheus/procfs/internal/util" 24 | ) 25 | 26 | // Swap represents an entry in /proc/swaps. 27 | type Swap struct { 28 | Filename string 29 | Type string 30 | Size int 31 | Used int 32 | Priority int 33 | } 34 | 35 | // Swaps returns a slice of all configured swap devices on the system. 36 | func (fs FS) Swaps() ([]*Swap, error) { 37 | data, err := util.ReadFileNoStat(fs.proc.Path("swaps")) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return parseSwaps(data) 42 | } 43 | 44 | func parseSwaps(info []byte) ([]*Swap, error) { 45 | swaps := []*Swap{} 46 | scanner := bufio.NewScanner(bytes.NewReader(info)) 47 | scanner.Scan() // ignore header line 48 | for scanner.Scan() { 49 | swapString := scanner.Text() 50 | parsedSwap, err := parseSwapString(swapString) 51 | if err != nil { 52 | return nil, err 53 | } 54 | swaps = append(swaps, parsedSwap) 55 | } 56 | 57 | err := scanner.Err() 58 | return swaps, err 59 | } 60 | 61 | func parseSwapString(swapString string) (*Swap, error) { 62 | var err error 63 | 64 | swapFields := strings.Fields(swapString) 65 | swapLength := len(swapFields) 66 | if swapLength < 5 { 67 | return nil, fmt.Errorf("%w: too few fields in swap string: %s", ErrFileParse, swapString) 68 | } 69 | 70 | swap := &Swap{ 71 | Filename: swapFields[0], 72 | Type: swapFields[1], 73 | } 74 | 75 | swap.Size, err = strconv.Atoi(swapFields[2]) 76 | if err != nil { 77 | return nil, fmt.Errorf("%w: invalid swap size: %s: %w", ErrFileParse, swapFields[2], err) 78 | } 79 | swap.Used, err = strconv.Atoi(swapFields[3]) 80 | if err != nil { 81 | return nil, fmt.Errorf("%w: invalid swap used: %s: %w", ErrFileParse, swapFields[3], err) 82 | } 83 | swap.Priority, err = strconv.Atoi(swapFields[4]) 84 | if err != nil { 85 | return nil, fmt.Errorf("%w: invalid swap priority: %s: %w", ErrFileParse, swapFields[4], err) 86 | } 87 | 88 | return swap, nil 89 | } 90 | -------------------------------------------------------------------------------- /swaps_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "reflect" 18 | "testing" 19 | ) 20 | 21 | func TestSwaps(t *testing.T) { 22 | fs, err := NewFS(procTestFixtures) 23 | if err != nil { 24 | t.Fatalf("failed to open procfs: %v", err) 25 | } 26 | 27 | swaps, err := fs.Swaps() 28 | if err != nil { 29 | t.Fatalf("failed to get swaps: %v", err) 30 | } 31 | 32 | if len(swaps) != 1 { 33 | t.Fatalf("expected 1 swap entry, got %d", len(swaps)) 34 | } 35 | swap := swaps[0] 36 | 37 | if swap.Filename != "/dev/dm-2" { 38 | t.Errorf("expected swap.Filename /dev/dm-2, got %s", swap.Filename) 39 | } 40 | if swap.Type != "partition" { 41 | t.Errorf("expected swap.Type partition, got %s", swap.Type) 42 | } 43 | if swap.Size != 131068 { 44 | t.Errorf("expected swap.Size 131068, got %d", swap.Size) 45 | } 46 | if swap.Used != 176 { 47 | t.Errorf("expected swap.Used 176, got %d", swap.Used) 48 | } 49 | if swap.Priority != -2 { 50 | t.Errorf("expected swap.Priority -2, got %d", swap.Priority) 51 | } 52 | } 53 | 54 | func TestParseSwapString(t *testing.T) { 55 | tests := []struct { 56 | name string 57 | s string 58 | swap *Swap 59 | invalid bool 60 | }{ 61 | { 62 | name: "device-mapper volume", 63 | s: "/dev/dm-2 partition 131068 1024 -2", 64 | invalid: false, 65 | swap: &Swap{ 66 | Filename: "/dev/dm-2", 67 | Type: "partition", 68 | Size: 131068, 69 | Used: 1024, 70 | Priority: -2, 71 | }, 72 | }, 73 | { 74 | name: "Swap file", 75 | s: "/foo file 1048572 0 -3", 76 | invalid: false, 77 | swap: &Swap{ 78 | Filename: "/foo", 79 | Type: "file", 80 | Size: 1048572, 81 | Used: 0, 82 | Priority: -3, 83 | }, 84 | }, 85 | { 86 | name: "Invalid number", 87 | s: "/dev/sda2 partition hello world -2", 88 | invalid: true, 89 | }, 90 | { 91 | name: "Not enough fields", 92 | s: "/dev/dm-2 partition 131068 1024", 93 | invalid: true, 94 | }, 95 | } 96 | 97 | for _, tt := range tests { 98 | t.Run(tt.name, func(t *testing.T) { 99 | swap, err := parseSwapString(tt.s) 100 | 101 | if tt.invalid && err == nil { 102 | t.Error("unexpected success") 103 | } 104 | if !tt.invalid && err != nil { 105 | t.Errorf("unexpected error: %v", err) 106 | } 107 | 108 | if !reflect.DeepEqual(tt.swap, swap) { 109 | t.Errorf("swap:\nwant:\n%+v\nhave:\n%+v", tt.swap, swap) 110 | } 111 | }) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /sysfs/.gitignore: -------------------------------------------------------------------------------- 1 | fixtures/ 2 | -------------------------------------------------------------------------------- /sysfs/class_cooling_device.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "path/filepath" 21 | "strconv" 22 | "strings" 23 | 24 | "github.com/prometheus/procfs/internal/util" 25 | ) 26 | 27 | // ClassCoolingDeviceStats contains info from files in /sys/class/thermal/cooling_device[0-9]* 28 | // for a single device. 29 | // https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt 30 | type ClassCoolingDeviceStats struct { 31 | Name string // The name of the cooling device. 32 | Type string // Type of the cooling device(processor/fan/...) 33 | MaxState int64 // Maximum cooling state of the cooling device 34 | CurState int64 // Current cooling state of the cooling device 35 | } 36 | 37 | func (fs FS) ClassCoolingDeviceStats() ([]ClassCoolingDeviceStats, error) { 38 | cds, err := filepath.Glob(fs.sys.Path("class/thermal/cooling_device[0-9]*")) 39 | if err != nil { 40 | return []ClassCoolingDeviceStats{}, err 41 | } 42 | 43 | var coolingDeviceStats = ClassCoolingDeviceStats{} 44 | stats := make([]ClassCoolingDeviceStats, len(cds)) 45 | for i, cd := range cds { 46 | cdName := strings.TrimPrefix(filepath.Base(cd), "cooling_device") 47 | 48 | coolingDeviceStats, err = parseCoolingDeviceStats(cd) 49 | if err != nil { 50 | return []ClassCoolingDeviceStats{}, err 51 | } 52 | 53 | coolingDeviceStats.Name = cdName 54 | stats[i] = coolingDeviceStats 55 | } 56 | return stats, nil 57 | } 58 | 59 | func parseCoolingDeviceStats(cd string) (ClassCoolingDeviceStats, error) { 60 | cdType, err := util.SysReadFile(filepath.Join(cd, "type")) 61 | if err != nil { 62 | return ClassCoolingDeviceStats{}, err 63 | } 64 | 65 | cdMaxStateString, err := util.SysReadFile(filepath.Join(cd, "max_state")) 66 | if err != nil { 67 | return ClassCoolingDeviceStats{}, err 68 | } 69 | cdMaxStateInt, err := strconv.ParseInt(cdMaxStateString, 10, 64) 70 | if err != nil { 71 | return ClassCoolingDeviceStats{}, err 72 | } 73 | 74 | // cur_state can be -1, eg intel powerclamp 75 | // https://www.kernel.org/doc/Documentation/thermal/intel_powerclamp.txt 76 | cdCurStateString, err := util.SysReadFile(filepath.Join(cd, "cur_state")) 77 | if err != nil { 78 | return ClassCoolingDeviceStats{}, err 79 | } 80 | 81 | cdCurStateInt, err := strconv.ParseInt(cdCurStateString, 10, 64) 82 | if err != nil { 83 | return ClassCoolingDeviceStats{}, err 84 | } 85 | 86 | return ClassCoolingDeviceStats{ 87 | Type: cdType, 88 | MaxState: cdMaxStateInt, 89 | CurState: cdCurStateInt, 90 | }, nil 91 | } 92 | -------------------------------------------------------------------------------- /sysfs/class_cooling_device_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | ) 23 | 24 | func TestClassCoolingDeviceStats(t *testing.T) { 25 | fs, err := NewFS(sysTestFixtures) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | coolingDeviceTest, err := fs.ClassCoolingDeviceStats() 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | classCoolingDeviceStats := []ClassCoolingDeviceStats{ 36 | { 37 | Name: "0", 38 | Type: "Processor", 39 | MaxState: 50, 40 | CurState: 0, 41 | }, 42 | { 43 | Name: "1", 44 | Type: "intel_powerclamp", 45 | MaxState: 27, 46 | CurState: -1, 47 | }, 48 | } 49 | 50 | if !reflect.DeepEqual(classCoolingDeviceStats, coolingDeviceTest) { 51 | t.Errorf("Result not correct: want %v, have %v", classCoolingDeviceStats, coolingDeviceTest) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sysfs/class_dmi_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestDMIClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.DMIClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | empty := "" 37 | biosDate := "04/12/2021" 38 | biosRelease := "2.2" 39 | biosVendor := "Dell Inc." 40 | biosVersion := "2.2.4" 41 | boardName := "07PXPY" 42 | boardSerial := ".7N62AI2.GRTCL6944100GP." 43 | boardVendor := "Dell Inc." 44 | boardVersion := "A01" 45 | chassisSerial := "7N62AI2" 46 | chassisType := "23" 47 | chassisVendor := "Dell Inc." 48 | productFamily := "PowerEdge" 49 | productName := "PowerEdge R6515" 50 | productSerial := "7N62AI2" 51 | productSKU := "SKU=NotProvided;ModelName=PowerEdge R6515" 52 | productUUID := "83340ca8-cb49-4474-8c29-d2088ca84dd9" 53 | systemVendor := "Dell Inc." 54 | 55 | want := &DMIClass{ 56 | BiosDate: &biosDate, 57 | BiosRelease: &biosRelease, 58 | BiosVendor: &biosVendor, 59 | BiosVersion: &biosVersion, 60 | BoardName: &boardName, 61 | BoardSerial: &boardSerial, 62 | BoardVendor: &boardVendor, 63 | BoardVersion: &boardVersion, 64 | ChassisAssetTag: &empty, 65 | ChassisSerial: &chassisSerial, 66 | ChassisType: &chassisType, 67 | ChassisVendor: &chassisVendor, 68 | ChassisVersion: &empty, 69 | ProductFamily: &productFamily, 70 | ProductName: &productName, 71 | ProductSerial: &productSerial, 72 | ProductSKU: &productSKU, 73 | ProductUUID: &productUUID, 74 | ProductVersion: &empty, 75 | SystemVendor: &systemVendor, 76 | } 77 | 78 | if diff := cmp.Diff(want, got); diff != "" { 79 | t.Fatalf("unexpected DMI class (-want +got):\n%s", diff) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sysfs/class_drm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "path/filepath" 21 | 22 | "github.com/prometheus/procfs/internal/util" 23 | ) 24 | 25 | func readDRMCardField(card, field string) (string, error) { 26 | return util.SysReadFile(filepath.Join(card, "device", field)) 27 | } 28 | -------------------------------------------------------------------------------- /sysfs/class_drm_amdgpu_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | ) 23 | 24 | func TestClassDRMCardAMDGPUStats(t *testing.T) { 25 | fs, err := NewFS(sysTestFixtures) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | drmTest, err := fs.ClassDRMCardAMDGPUStats() 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | classDRMCardStats := []ClassDRMCardAMDGPUStats{ 36 | { 37 | Name: "card0", 38 | GPUBusyPercent: 4, 39 | MemoryGTTSize: 8573157376, 40 | MemoryGTTUsed: 144560128, 41 | MemoryVisibleVRAMSize: 8573157376, 42 | MemoryVisibleVRAMUsed: 1490378752, 43 | MemoryVRAMSize: 8573157376, 44 | MemoryVRAMUsed: 1490378752, 45 | MemoryVRAMVendor: "samsung", 46 | PowerDPMForcePerformanceLevel: "manual", 47 | UniqueID: "0123456789abcdef", 48 | }, 49 | { 50 | Name: "card1", 51 | GPUBusyPercent: 0, 52 | MemoryGTTSize: 0, 53 | MemoryGTTUsed: 0, 54 | MemoryVisibleVRAMSize: 0, 55 | MemoryVisibleVRAMUsed: 0, 56 | MemoryVRAMSize: 0, 57 | MemoryVRAMUsed: 0, 58 | }, 59 | } 60 | 61 | if !reflect.DeepEqual(classDRMCardStats, drmTest) { 62 | t.Errorf("Result not correct: want %v, have %v", classDRMCardStats, drmTest) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sysfs/class_drm_card_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestClassDRMCard(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.DRMCardClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | want := DRMCardClass{ 37 | "card0": DRMCard{ 38 | Name: "card0", 39 | Driver: "amdgpu", 40 | Ports: map[string]DRMCardPort{}, 41 | }, 42 | "card1": DRMCard{ 43 | Name: "card1", 44 | Driver: "i915", 45 | Ports: map[string]DRMCardPort{ 46 | "card1-DP-1": { 47 | Name: "card1-DP-1", 48 | DPMS: "Off", 49 | Enabled: "disabled", 50 | Status: "disconnected", 51 | }, 52 | "card1-DP-5": { 53 | Name: "card1-DP-5", 54 | DPMS: "On", 55 | Enabled: "enabled", 56 | Status: "connected", 57 | }, 58 | }, 59 | }, 60 | } 61 | 62 | if diff := cmp.Diff(want, got); diff != "" { 63 | t.Fatalf("unexpected DRMCard class (-want +got):\n%s", diff) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sysfs/class_nvme.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "path/filepath" 23 | 24 | "github.com/prometheus/procfs/internal/util" 25 | ) 26 | 27 | const nvmeClassPath = "class/nvme" 28 | 29 | // NVMeDevice contains info from files in /sys/class/nvme for a single NVMe device. 30 | type NVMeDevice struct { 31 | Name string 32 | Serial string // /sys/class/nvme//serial 33 | Model string // /sys/class/nvme//model 34 | State string // /sys/class/nvme//state 35 | FirmwareRevision string // /sys/class/nvme//firmware_rev 36 | } 37 | 38 | // NVMeClass is a collection of every NVMe device in /sys/class/nvme. 39 | // 40 | // The map keys are the names of the NVMe devices. 41 | type NVMeClass map[string]NVMeDevice 42 | 43 | // NVMeClass returns info for all NVMe devices read from /sys/class/nvme. 44 | func (fs FS) NVMeClass() (NVMeClass, error) { 45 | path := fs.sys.Path(nvmeClassPath) 46 | 47 | dirs, err := os.ReadDir(path) 48 | if err != nil { 49 | return nil, fmt.Errorf("failed to list NVMe devices at %q: %w", path, err) 50 | } 51 | 52 | nc := make(NVMeClass, len(dirs)) 53 | for _, d := range dirs { 54 | device, err := fs.parseNVMeDevice(d.Name()) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | nc[device.Name] = *device 60 | } 61 | 62 | return nc, nil 63 | } 64 | 65 | // Parse one NVMe device. 66 | func (fs FS) parseNVMeDevice(name string) (*NVMeDevice, error) { 67 | path := fs.sys.Path(nvmeClassPath, name) 68 | device := NVMeDevice{Name: name} 69 | 70 | for _, f := range [...]string{"firmware_rev", "model", "serial", "state"} { 71 | name := filepath.Join(path, f) 72 | value, err := util.SysReadFile(name) 73 | if err != nil { 74 | return nil, fmt.Errorf("failed to read file %q: %w", name, err) 75 | } 76 | 77 | switch f { 78 | case "firmware_rev": 79 | device.FirmwareRevision = value 80 | case "model": 81 | device.Model = value 82 | case "serial": 83 | device.Serial = value 84 | case "state": 85 | device.State = value 86 | } 87 | } 88 | 89 | return &device, nil 90 | } 91 | -------------------------------------------------------------------------------- /sysfs/class_nvme_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestNVMeClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.NVMeClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | want := NVMeClass{ 37 | "nvme0": NVMeDevice{ 38 | Name: "nvme0", 39 | FirmwareRevision: "1B2QEXP7", 40 | Model: "Samsung SSD 970 PRO 512GB", 41 | Serial: "S680HF8N190894I", 42 | State: "live", 43 | }, 44 | } 45 | 46 | if diff := cmp.Diff(want, got); diff != "" { 47 | t.Fatalf("unexpected NVMe class (-want +got):\n%s", diff) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sysfs/class_power_supply_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestPowerSupplyClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatalf("failed to open filesystem: %v", err) 29 | } 30 | 31 | got, err := fs.PowerSupplyClass() 32 | if err != nil { 33 | t.Fatalf("failed to parse power supply class: %v", err) 34 | } 35 | 36 | var ( 37 | acOnline int64 38 | bat0Capacity int64 = 98 39 | bat0CycleCount int64 40 | bat0EnergyFull int64 = 50060000 41 | bat0EnergyFullDesign int64 = 47520000 42 | bat0EnergyNow int64 = 49450000 43 | bat0PowerNow int64 = 4830000 44 | bat0Present int64 = 1 45 | bat0VoltageMinDesign int64 = 10800000 46 | bat0VoltageNow int64 = 12229000 47 | ) 48 | 49 | want := PowerSupplyClass{ 50 | "AC": { 51 | Name: "AC", 52 | Type: "Mains", 53 | Online: &acOnline, 54 | }, 55 | "BAT0": { 56 | Name: "BAT0", 57 | Capacity: &bat0Capacity, 58 | CapacityLevel: "Normal", 59 | CycleCount: &bat0CycleCount, 60 | EnergyFull: &bat0EnergyFull, 61 | EnergyFullDesign: &bat0EnergyFullDesign, 62 | EnergyNow: &bat0EnergyNow, 63 | Manufacturer: "LGC", 64 | ModelName: "LNV-45N1", 65 | PowerNow: &bat0PowerNow, 66 | Present: &bat0Present, 67 | SerialNumber: "38109", 68 | Status: "Discharging", 69 | Technology: "Li-ion", 70 | Type: "Battery", 71 | VoltageMinDesign: &bat0VoltageMinDesign, 72 | VoltageNow: &bat0VoltageNow, 73 | }, 74 | } 75 | 76 | if diff := cmp.Diff(want, got); diff != "" { 77 | t.Fatalf("unexpected power supply class (-want +got):\n%s", diff) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sysfs/class_powercap_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | ) 23 | 24 | func TestGetRaplZones(t *testing.T) { 25 | fs, err := NewFS(sysTestFixtures) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | zones, err := GetRaplZones(fs) 31 | if err != nil || zones == nil { 32 | t.Fatal(err) 33 | } 34 | } 35 | 36 | func TestNoRaplFiles(t *testing.T) { 37 | // use a bad (but existing) fs path 38 | fs, err := NewFS(filepath.Join(sysTestFixtures, "class")) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | zones, err := GetRaplZones(fs) 43 | // expect failure 44 | if err == nil || zones != nil { 45 | t.Fatal(err) 46 | } 47 | } 48 | 49 | func TestNewRaplValues(t *testing.T) { 50 | fs, err := NewFS(sysTestFixtures) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | zones, err := GetRaplZones(fs) 56 | if err != nil || zones == nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if len(zones) != 3 { 61 | t.Fatal("wrong number of RAPL values") 62 | } 63 | microjoules, err := zones[0].GetEnergyMicrojoules() 64 | if err != nil { 65 | t.Fatal("couldn't read microjoules") 66 | } 67 | if microjoules != 240422366267 { 68 | t.Fatal("wrong microjoule number") 69 | } 70 | if zones[2].Index != 10 { 71 | t.Fatal("wrong index number") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /sysfs/class_sas_host_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestSASHostClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.SASHostClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | want := SASHostClass{ 37 | "host11": &SASHost{ 38 | Name: "host11", 39 | SASPhys: []string{ 40 | "phy-11:10", "phy-11:11", "phy-11:12", "phy-11:13", 41 | "phy-11:14", "phy-11:15", "phy-11:7", "phy-11:8", 42 | "phy-11:9", 43 | }, 44 | SASPorts: []string{ 45 | "port-11:0", "port-11:1", "port-11:2", 46 | }, 47 | }, 48 | } 49 | 50 | if diff := cmp.Diff(want, got); diff != "" { 51 | t.Fatalf("unexpected SASHost class (-want +got):\n%s", diff) 52 | } 53 | } 54 | 55 | func TestSASHostGetByName(t *testing.T) { 56 | fs, err := NewFS(sysTestFixtures) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | 61 | hc, err := fs.SASHostClass() 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | 66 | want := "host11" 67 | got := hc.GetByName("host11").Name 68 | if diff := cmp.Diff(want, got); diff != "" { 69 | t.Fatalf("unexpected SASHost class (-want +got):\n%s", diff) 70 | } 71 | 72 | // Doesn't exist. 73 | got2 := hc.GetByName("host12") 74 | if got2 != nil { 75 | t.Fatalf("unexpected GetByName response: got %v want nil", got2) 76 | } 77 | } 78 | 79 | func TestSASHostGetByPhy(t *testing.T) { 80 | fs, err := NewFS(sysTestFixtures) 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | 85 | hc, err := fs.SASHostClass() 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | 90 | want := "host11" 91 | got := hc.GetByPhy("phy-11:11").Name 92 | if diff := cmp.Diff(want, got); diff != "" { 93 | t.Fatalf("unexpected SASHost class (-want +got):\n%s", diff) 94 | } 95 | 96 | // Doesn't exist. 97 | got2 := hc.GetByPhy("phy-12:0") 98 | if got2 != nil { 99 | t.Fatalf("unexpected GetByPhy response: got %v want nil", got2) 100 | } 101 | } 102 | 103 | func TestSASHostGetByPort(t *testing.T) { 104 | fs, err := NewFS(sysTestFixtures) 105 | if err != nil { 106 | t.Fatal(err) 107 | } 108 | 109 | hc, err := fs.SASHostClass() 110 | if err != nil { 111 | t.Fatal(err) 112 | } 113 | 114 | want := "host11" 115 | got := hc.GetByPort("port-11:0").Name 116 | if diff := cmp.Diff(want, got); diff != "" { 117 | t.Fatalf("unexpected SASHost class (-want +got):\n%s", diff) 118 | } 119 | 120 | // Doesn't exist. 121 | got2 := hc.GetByPort("port-12:0") 122 | if got2 != nil { 123 | t.Fatalf("unexpected GetByPhy response: got %v want nil", got2) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sysfs/class_scsitape_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestSCSITapeClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.SCSITapeClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | want := SCSITapeClass{ 37 | "st0": SCSITape{ 38 | Name: "st0", 39 | Counters: SCSITapeCounters{ 40 | WriteNs: 5233597394395, 41 | ReadByteCnt: 979383912, 42 | IoNs: 9247011087720, 43 | WriteCnt: 53772916, 44 | WriteByteCnt: 1496246784000, 45 | ResidCnt: 19, 46 | ReadNs: 33788355744, 47 | InFlight: 1, 48 | OtherCnt: 1409, 49 | ReadCnt: 3741, 50 | }, 51 | }, 52 | } 53 | 54 | if diff := cmp.Diff(want, got); diff != "" { 55 | t.Fatalf("unexpected SCSITape class (-want +got):\n%s", diff) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sysfs/class_thermal_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | 23 | "github.com/prometheus/procfs/internal/util" 24 | ) 25 | 26 | func TestClassThermalZoneStats(t *testing.T) { 27 | fs, err := NewFS(sysTestFixtures) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | thermalTest, err := fs.ClassThermalZoneStats() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | enabled := util.ParseBool("enabled") 38 | passive := uint64(0) 39 | 40 | classThermalZoneStats := []ClassThermalZoneStats{ 41 | { 42 | Name: "0", 43 | Type: "bcm2835_thermal", 44 | Policy: "step_wise", 45 | Temp: 49925, 46 | Mode: nil, 47 | Passive: nil, 48 | }, 49 | { 50 | Name: "1", 51 | Type: "acpitz", 52 | Policy: "step_wise", 53 | Temp: -44000, 54 | Mode: enabled, 55 | Passive: &passive, 56 | }, 57 | } 58 | 59 | if !reflect.DeepEqual(classThermalZoneStats, thermalTest) { 60 | t.Errorf("Result not correct: want %v, have %v", classThermalZoneStats, thermalTest) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sysfs/class_watchdog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func TestWatchdogClass(t *testing.T) { 26 | fs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | got, err := fs.WatchdogClass() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | var ( 37 | bootstatus int64 = 1 38 | fwVersion int64 = 2 39 | nowayout int64 40 | timeleft int64 = 300 41 | timeout int64 = 60 42 | pretimeout int64 = 120 43 | accessCs0 int64 44 | 45 | options = "0x8380" 46 | identity = "Software Watchdog" 47 | state = "active" 48 | status = "0x8000" 49 | pretimeoutGovernor = "noop" 50 | ) 51 | 52 | want := WatchdogClass{ 53 | "watchdog0": { 54 | Name: "watchdog0", 55 | Bootstatus: &bootstatus, 56 | Options: &options, 57 | FwVersion: &fwVersion, 58 | Identity: &identity, 59 | Nowayout: &nowayout, 60 | State: &state, 61 | Status: &status, 62 | Timeleft: &timeleft, 63 | Timeout: &timeout, 64 | Pretimeout: &pretimeout, 65 | PretimeoutGovernor: &pretimeoutGovernor, 66 | AccessCs0: &accessCs0, 67 | }, 68 | "watchdog1": { 69 | Name: "watchdog1", 70 | }, 71 | } 72 | 73 | if diff := cmp.Diff(want, got); diff != "" { 74 | t.Fatalf("unexpected watchdog class (-want +got):\n%s", diff) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /sysfs/clocksource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "path/filepath" 21 | "strings" 22 | 23 | "github.com/prometheus/procfs/internal/util" 24 | ) 25 | 26 | // ClockSource contains metrics related to the clock source. 27 | type ClockSource struct { 28 | Name string 29 | Available []string 30 | Current string 31 | } 32 | 33 | // ClockSources returns clocksource information including current and available clocksources 34 | // read from '/sys/devices/system/clocksource'. 35 | func (fs FS) ClockSources() ([]ClockSource, error) { 36 | 37 | clocksourcePaths, err := filepath.Glob(fs.sys.Path("devices/system/clocksource/clocksource[0-9]*")) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | clocksources := make([]ClockSource, len(clocksourcePaths)) 43 | for i, clocksourcePath := range clocksourcePaths { 44 | clocksourceName := strings.TrimPrefix(filepath.Base(clocksourcePath), "clocksource") 45 | 46 | clocksource, err := parseClocksource(clocksourcePath) 47 | if err != nil { 48 | return nil, err 49 | } 50 | clocksource.Name = clocksourceName 51 | clocksources[i] = *clocksource 52 | } 53 | 54 | return clocksources, nil 55 | } 56 | 57 | func parseClocksource(clocksourcePath string) (*ClockSource, error) { 58 | 59 | stringFiles := []string{ 60 | "available_clocksource", 61 | "current_clocksource", 62 | } 63 | stringOut := make([]string, len(stringFiles)) 64 | var err error 65 | 66 | for i, f := range stringFiles { 67 | stringOut[i], err = util.SysReadFile(filepath.Join(clocksourcePath, f)) 68 | if err != nil { 69 | return &ClockSource{}, err 70 | } 71 | } 72 | 73 | return &ClockSource{ 74 | Available: strings.Fields(stringOut[0]), 75 | Current: stringOut[1], 76 | }, nil 77 | } 78 | -------------------------------------------------------------------------------- /sysfs/clocksource_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | ) 23 | 24 | func TestNewClocksource(t *testing.T) { 25 | fs, err := NewFS(sysTestFixtures) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | c, err := fs.ClockSources() 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | clocksources := []ClockSource{ 36 | { 37 | Name: "0", 38 | Available: []string{"tsc", "hpet", "acpi_pm"}, 39 | Current: "tsc", 40 | }, 41 | } 42 | 43 | if !reflect.DeepEqual(clocksources, c) { 44 | t.Errorf("Result not correct: want %v, have %v", clocksources, c) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sysfs/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | // Package sysfs provides functions to retrieve system and kernel metrics 18 | // from the pseudo-filesystem sys. 19 | package sysfs 20 | -------------------------------------------------------------------------------- /sysfs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "github.com/prometheus/procfs/internal/fs" 21 | ) 22 | 23 | // FS represents the pseudo-filesystem sys, which provides an interface to 24 | // kernel data structures. 25 | type FS struct { 26 | sys fs.FS 27 | } 28 | 29 | // DefaultMountPoint is the common mount point of the sys filesystem. 30 | const DefaultMountPoint = fs.DefaultSysMountPoint 31 | 32 | // NewDefaultFS returns a new FS mounted under the default mountPoint. It will error 33 | // if the mount point can't be read. 34 | func NewDefaultFS() (FS, error) { 35 | return NewFS(DefaultMountPoint) 36 | } 37 | 38 | // NewFS returns a new FS mounted under the given mountPoint. It will error 39 | // if the mount point can't be read. 40 | func NewFS(mountPoint string) (FS, error) { 41 | fs, err := fs.NewFS(mountPoint) 42 | if err != nil { 43 | return FS{}, err 44 | } 45 | return FS{fs}, nil 46 | } 47 | -------------------------------------------------------------------------------- /sysfs/fs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import "testing" 20 | 21 | const ( 22 | sysTestFixtures = "testdata/fixtures/sys" 23 | ) 24 | 25 | func TestNewFS(t *testing.T) { 26 | if _, err := NewFS("foobar"); err == nil { 27 | t.Error("want NewFS to fail for non-existing mount point") 28 | } 29 | 30 | if _, err := NewFS("doc.go"); err == nil { 31 | t.Error("want NewFS to fail if mount point is not a directory") 32 | } 33 | 34 | if _, err := NewFS(sysTestFixtures); err != nil { 35 | t.Error("want NewFS to succeed if mount point exists") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sysfs/net_class_aer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | ) 23 | 24 | func TestAerCountersByIface(t *testing.T) { 25 | fs, err := NewFS(sysTestFixtures) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | _, err = fs.AerCountersByIface("non-existent") 31 | if err == nil { 32 | t.Fatal("expected error, have none") 33 | } 34 | 35 | device, err := fs.AerCountersByIface("eth0") 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | if device.Name != "eth0" { 41 | t.Errorf("Found unexpected device, want %s, have %s", "eth0", device.Name) 42 | } 43 | } 44 | 45 | func TestAerCounters(t *testing.T) { 46 | fs, err := NewFS(sysTestFixtures) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | ac, _ := fs.AerCounters() 52 | aerCounters := AllAerCounters{ 53 | "eth0": AerCounters{ 54 | Name: "eth0", 55 | Correctable: CorrectableAerCounters{ 56 | RxErr: 1, 57 | BadTLP: 2, 58 | BadDLLP: 3, 59 | Rollover: 4, 60 | Timeout: 5, 61 | NonFatalErr: 6, 62 | CorrIntErr: 7, 63 | HeaderOF: 8, 64 | }, 65 | Fatal: UncorrectableAerCounters{ 66 | Undefined: 10, 67 | DLP: 11, 68 | SDES: 12, 69 | TLP: 13, 70 | FCP: 14, 71 | CmpltTO: 15, 72 | CmpltAbrt: 16, 73 | UnxCmplt: 17, 74 | RxOF: 18, 75 | MalfTLP: 19, 76 | ECRC: 20, 77 | UnsupReq: 21, 78 | ACSViol: 22, 79 | UncorrIntErr: 23, 80 | BlockedTLP: 24, 81 | AtomicOpBlocked: 25, 82 | TLPBlockedErr: 26, 83 | PoisonTLPBlocked: 27, 84 | }, 85 | NonFatal: UncorrectableAerCounters{ 86 | Undefined: 30, 87 | DLP: 31, 88 | SDES: 32, 89 | TLP: 33, 90 | FCP: 34, 91 | CmpltTO: 35, 92 | CmpltAbrt: 36, 93 | UnxCmplt: 37, 94 | RxOF: 38, 95 | MalfTLP: 39, 96 | ECRC: 40, 97 | UnsupReq: 41, 98 | ACSViol: 42, 99 | UncorrIntErr: 43, 100 | BlockedTLP: 44, 101 | AtomicOpBlocked: 45, 102 | TLPBlockedErr: 46, 103 | PoisonTLPBlocked: 47, 104 | }, 105 | }, 106 | } 107 | 108 | if !reflect.DeepEqual(aerCounters, ac) { 109 | t.Errorf("Result not correct: want %v, have %v", aerCounters, ac) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /sysfs/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /sysfs/vmstat_numa_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestParseVMStatNUMA(t *testing.T) { 24 | fs, err := NewFS(sysTestFixtures) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | vmstat, err := fs.VMStatNUMA() 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | if want, got := uint64(1), vmstat[1].NrFreePages; want != got { 34 | t.Errorf("want vmstat stat nr_free_pages value %d, got %d", want, got) 35 | } 36 | 37 | if want, got := uint64(5), vmstat[1].NrZoneActiveFile; want != got { 38 | t.Errorf("want numa stat nr_zone_active_file %d, got %d", want, got) 39 | } 40 | if want, got := uint64(7), vmstat[2].NrFreePages; want != got { 41 | t.Errorf("want vmstat stat nr_free_pages value %d, got %d", want, got) 42 | } 43 | 44 | if want, got := uint64(11), vmstat[2].NrZoneActiveFile; want != got { 45 | t.Errorf("want numa stat nr_zone_active_file %d, got %d", want, got) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sysfs/vulnerability_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build linux 15 | // +build linux 16 | 17 | package sysfs 18 | 19 | import ( 20 | "fmt" 21 | "reflect" 22 | "testing" 23 | ) 24 | 25 | func TestFS_CPUVulnerabilities(t *testing.T) { 26 | sysFs, err := NewFS(sysTestFixtures) 27 | if err != nil { 28 | t.Fatal(fmt.Errorf("failed to get sysfs FS: %w", err)) 29 | } 30 | got, err := sysFs.CPUVulnerabilities() 31 | if err != nil { 32 | t.Fatal(fmt.Errorf("failed to parse sysfs vulnerabilities files: %w", err)) 33 | } 34 | 35 | tests := []struct { 36 | name string 37 | vulnerabilityName string 38 | want *Vulnerability 39 | wantErr bool 40 | }{ 41 | {"Not affected", "tsx_async_abort", &Vulnerability{CodeName: "tsx_async_abort", State: VulnerabilityStateNotAffected, Mitigation: ""}, false}, 42 | {"Mitigation simple string", "spec_store_bypass", &Vulnerability{CodeName: "spec_store_bypass", State: VulnerabilityStateMitigation, Mitigation: "Speculative Store Bypass disabled via prctl"}, false}, 43 | {"Mitigation special chars", "retbleed", &Vulnerability{CodeName: "retbleed", State: VulnerabilityStateMitigation, Mitigation: "untrained return thunk; SMT enabled with STIBP protection"}, false}, 44 | {"Mitigation more special chars", "spectre_v1", &Vulnerability{CodeName: "spectre_v1", State: VulnerabilityStateMitigation, Mitigation: "usercopy/swapgs barriers and __user pointer sanitization"}, false}, //nolint:misspell 45 | {"Mitigation with multiple subsections", "spectre_v2", &Vulnerability{CodeName: "spectre_v2", State: VulnerabilityStateMitigation, Mitigation: "Retpolines, IBPB: conditional, STIBP: always-on, RSB filling, PBRSB-eIBRS: Not affected"}, false}, //nolint:misspell 46 | {"Vulnerable", "mds", &Vulnerability{CodeName: "mds", State: VulnerabilityStateVulnerable, Mitigation: ""}, false}, 47 | {"Vulnerable with mitigation available", "mmio_stale_data", &Vulnerability{CodeName: "mmio_stale_data", State: VulnerabilityStateVulnerable, Mitigation: "Clear CPU buffers attempted, no microcode"}, false}, 48 | {"Unknown", "srbds", &Vulnerability{CodeName: "srbds", State: VulnerabilityStateUnknown, Mitigation: "Dependent on hypervisor status"}, false}, 49 | {"Unknown with unparseable mitigation", "itlb_multihit", &Vulnerability{CodeName: "itlb_multihit", State: VulnerabilityStateUnknown, Mitigation: "KVM: Mitigation: VMX unsupported"}, false}, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | gotVulnerability, ok := got[tt.vulnerabilityName] 54 | if !ok && !tt.wantErr { 55 | t.Errorf("CPUVulnerabilities() vulnerability %s not found", tt.vulnerabilityName) 56 | } 57 | if !reflect.DeepEqual(gotVulnerability, tt.want) { 58 | t.Errorf("CPUVulnerabilities() gotVulnerability = %v, want %v", gotVulnerability, tt.want) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /testdata/fixtures.ttar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prometheus/procfs/588b88c23f12c329a3e83376ba3dbc35fff85366/testdata/fixtures.ttar -------------------------------------------------------------------------------- /thread.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package procfs 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "strconv" 20 | 21 | fsi "github.com/prometheus/procfs/internal/fs" 22 | ) 23 | 24 | // Provide access to /proc/PID/task/TID files, for thread specific values. Since 25 | // such files have the same structure as /proc/PID/ ones, the data structures 26 | // and the parsers for the latter may be reused. 27 | 28 | // AllThreads returns a list of all currently available threads under /proc/PID. 29 | func AllThreads(pid int) (Procs, error) { 30 | fs, err := NewFS(DefaultMountPoint) 31 | if err != nil { 32 | return Procs{}, err 33 | } 34 | return fs.AllThreads(pid) 35 | } 36 | 37 | // AllThreads returns a list of all currently available threads for PID. 38 | func (fs FS) AllThreads(pid int) (Procs, error) { 39 | taskPath := fs.proc.Path(strconv.Itoa(pid), "task") 40 | d, err := os.Open(taskPath) 41 | if err != nil { 42 | return Procs{}, err 43 | } 44 | defer d.Close() 45 | 46 | names, err := d.Readdirnames(-1) 47 | if err != nil { 48 | return Procs{}, fmt.Errorf("%w: could not read %q: %w", ErrFileRead, d.Name(), err) 49 | } 50 | 51 | t := Procs{} 52 | for _, n := range names { 53 | tid, err := strconv.ParseInt(n, 10, 64) 54 | if err != nil { 55 | continue 56 | } 57 | 58 | t = append(t, Proc{PID: int(tid), fs: FS{fsi.FS(taskPath), fs.isReal}}) 59 | } 60 | 61 | return t, nil 62 | } 63 | 64 | // Thread returns a process for a given PID, TID. 65 | func (fs FS) Thread(pid, tid int) (Proc, error) { 66 | taskPath := fs.proc.Path(strconv.Itoa(pid), "task") 67 | if _, err := os.Stat(taskPath); err != nil { 68 | return Proc{}, err 69 | } 70 | return Proc{PID: tid, fs: FS{fsi.FS(taskPath), fs.isReal}}, nil 71 | } 72 | 73 | // Thread returns a process for a given TID of Proc. 74 | func (proc Proc) Thread(tid int) (Proc, error) { 75 | tfs := FS{fsi.FS(proc.path("task")), proc.fs.isReal} 76 | if _, err := os.Stat(tfs.proc.Path(strconv.Itoa(tid))); err != nil { 77 | return Proc{}, err 78 | } 79 | return Proc{PID: tid, fs: tfs}, nil 80 | } 81 | -------------------------------------------------------------------------------- /vm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //go:build !windows 15 | // +build !windows 16 | 17 | package procfs 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/google/go-cmp/cmp" 23 | ) 24 | 25 | func newPInt64(i int64) *int64 { 26 | return &i 27 | } 28 | 29 | func TestVM(t *testing.T) { 30 | fs, err := NewFS(procTestFixtures) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | got, err := fs.VM() 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | zeroPointer := newPInt64(0) 39 | lowmemreserveratio := []*int64{newPInt64(256), newPInt64(256), newPInt64(32), zeroPointer, zeroPointer} 40 | want := &VM{ 41 | AdminReserveKbytes: newPInt64(8192), 42 | BlockDump: zeroPointer, 43 | CompactUnevictableAllowed: newPInt64(1), 44 | DirtyBackgroundBytes: zeroPointer, 45 | DirtyBackgroundRatio: newPInt64(10), 46 | DirtyBytes: zeroPointer, 47 | DirtyExpireCentisecs: newPInt64(3000), 48 | DirtyRatio: newPInt64(20), 49 | DirtytimeExpireSeconds: newPInt64(43200), 50 | DirtyWritebackCentisecs: newPInt64(500), 51 | DropCaches: zeroPointer, 52 | ExtfragThreshold: newPInt64(500), 53 | HugetlbShmGroup: zeroPointer, 54 | LaptopMode: newPInt64(5), 55 | LegacyVaLayout: zeroPointer, 56 | LowmemReserveRatio: lowmemreserveratio, 57 | MaxMapCount: newPInt64(65530), 58 | MemoryFailureEarlyKill: zeroPointer, 59 | MemoryFailureRecovery: newPInt64(1), 60 | MinFreeKbytes: newPInt64(67584), 61 | MinSlabRatio: newPInt64(5), 62 | MinUnmappedRatio: newPInt64(1), 63 | MmapMinAddr: newPInt64(65536), 64 | NumaStat: newPInt64(1), 65 | NumaZonelistOrder: "Node", 66 | NrHugepages: zeroPointer, 67 | NrHugepagesMempolicy: zeroPointer, 68 | NrOvercommitHugepages: zeroPointer, 69 | OomDumpTasks: newPInt64(1), 70 | OomKillAllocatingTask: zeroPointer, 71 | OvercommitKbytes: zeroPointer, 72 | OvercommitMemory: zeroPointer, 73 | OvercommitRatio: newPInt64(50), 74 | PageCluster: newPInt64(3), 75 | PanicOnOom: zeroPointer, 76 | PercpuPagelistFraction: zeroPointer, 77 | StatInterval: newPInt64(1), 78 | Swappiness: newPInt64(60), 79 | UserReserveKbytes: newPInt64(131072), 80 | VfsCachePressure: newPInt64(100), 81 | WatermarkBoostFactor: newPInt64(15000), 82 | WatermarkScaleFactor: newPInt64(10), 83 | ZoneReclaimMode: zeroPointer, 84 | } 85 | if diff := cmp.Diff(want, got); diff != "" { 86 | t.Fatalf("unexpected power supply class (-want +got):\n%s", diff) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /xfs/testdata/fixtures: -------------------------------------------------------------------------------- 1 | ../../testdata/fixtures -------------------------------------------------------------------------------- /xfs/xfs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Prometheus Authors 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package xfs provides access to statistics exposed by the XFS filesystem. 15 | package xfs_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/prometheus/procfs/xfs" 21 | ) 22 | 23 | func TestReadProcStat(t *testing.T) { 24 | xfs, err := xfs.NewFS("testdata/fixtures/proc", "testdata/fixtures/sys") 25 | if err != nil { 26 | t.Fatalf("failed to access xfs fs: %v", err) 27 | } 28 | stats, err := xfs.ProcStat() 29 | if err != nil { 30 | t.Fatalf("failed to parse XFS stats: %v", err) 31 | } 32 | 33 | // Very lightweight test just to sanity check the path used 34 | // to open XFS stats. Heavier tests in package xfs. 35 | if want, got := uint32(92447), stats.ExtentAllocation.ExtentsAllocated; want != got { 36 | t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got) 37 | } 38 | } 39 | 40 | func TestReadSysStats(t *testing.T) { 41 | xfs, err := xfs.NewFS("testdata/fixtures/proc", "testdata/fixtures/sys") 42 | if err != nil { 43 | t.Fatalf("failed to access xfs fs: %v", err) 44 | } 45 | stats, err := xfs.SysStats() 46 | if err != nil { 47 | t.Fatalf("failed to parse XFS stats: %v", err) 48 | } 49 | 50 | tests := []struct { 51 | name string 52 | allocated uint32 53 | }{ 54 | { 55 | name: "sda1", 56 | allocated: 1, 57 | }, 58 | { 59 | name: "sdb1", 60 | allocated: 2, 61 | }, 62 | } 63 | 64 | const expect = 2 65 | 66 | if l := len(stats); l != expect { 67 | t.Fatalf("unexpected number of XFS stats: %d", l) 68 | } 69 | if l := len(tests); l != expect { 70 | t.Fatalf("unexpected number of tests: %d", l) 71 | } 72 | 73 | for i, tt := range tests { 74 | if want, got := tt.name, stats[i].Name; want != got { 75 | t.Errorf("unexpected stats name:\nwant: %q\nhave: %q", want, got) 76 | } 77 | 78 | if want, got := tt.allocated, stats[i].ExtentAllocation.ExtentsAllocated; want != got { 79 | t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got) 80 | } 81 | } 82 | } 83 | --------------------------------------------------------------------------------