Skip to content

Commit

Permalink
add /proc/interrupts (#475)
Browse files Browse the repository at this point in the history
Signed-off-by: Furkan <[email protected]>
  • Loading branch information
Dentrax authored Dec 17, 2022
1 parent 88f86e5 commit 42aaf8e
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 0 deletions.
98 changes: 98 additions & 0 deletions proc_interrupts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2022 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"strconv"
"strings"

"github.com/prometheus/procfs/internal/util"
)

// Interrupt represents a single interrupt line.
type Interrupt struct {
// Info is the type of interrupt.
Info string
// Devices is the name of the device that is located at that IRQ
Devices string
// Values is the number of interrupts per CPU.
Values []string
}

// Interrupts models the content of /proc/interrupts. Key is the IRQ number.
// - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-interrupts
// - https://raspberrypi.stackexchange.com/questions/105802/explanation-of-proc-interrupts-output
type Interrupts map[string]Interrupt

// Interrupts creates a new instance from a given Proc instance.
func (p Proc) Interrupts() (Interrupts, error) {
data, err := util.ReadFileNoStat(p.path("interrupts"))
if err != nil {
return nil, err
}
return parseInterrupts(bytes.NewReader(data))
}

func parseInterrupts(r io.Reader) (Interrupts, error) {
var (
interrupts = Interrupts{}
scanner = bufio.NewScanner(r)
)

if !scanner.Scan() {
return nil, errors.New("interrupts empty")
}
cpuNum := len(strings.Fields(scanner.Text())) // one header per cpu

for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) == 0 { // skip empty lines
continue
}
if len(parts) < 2 {
return nil, fmt.Errorf("not enough fields in interrupts (expected at least 2 fields but got %d): %s", len(parts), parts)
}
intName := parts[0][:len(parts[0])-1] // remove trailing :

if len(parts) == 2 {
interrupts[intName] = Interrupt{
Info: "",
Devices: "",
Values: []string{
parts[1],
},
}
continue
}

intr := Interrupt{
Values: parts[1 : cpuNum+1],
}

if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt
intr.Info = parts[cpuNum+1]
intr.Devices = strings.Join(parts[cpuNum+2:], " ")
} else {
intr.Info = strings.Join(parts[cpuNum+1:], " ")
}
interrupts[intName] = intr
}

return interrupts, scanner.Err()
}
94 changes: 94 additions & 0 deletions proc_interrupts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2022 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
"reflect"
"testing"
)

func TestProcInterrupts(t *testing.T) {
p, err := getProcFixtures(t).Proc(26231)
if err != nil {
t.Fatal(err)
}

interrupts, err := p.Interrupts()
if err != nil {
t.Fatal(err)
}

if want, have := 47, len(interrupts); want != have {
t.Errorf("want length %d, have %d", want, have)
}

for _, test := range []struct {
name string
irq string
want Interrupt
}{
{
name: "first line",
irq: "0",
want: Interrupt{
Info: "IO-APIC",
Devices: "2-edge timer",
Values: []string{"49", "0", "0", "0"},
},
},
{
name: "last line",
irq: "PIW",
want: Interrupt{
Info: "Posted-interrupt wakeup event",
Devices: "",
Values: []string{"0", "0", "0", "0"},
},
},
{
name: "empty devices",
irq: "LOC",
want: Interrupt{
Info: "Local timer interrupts",
Devices: "",
Values: []string{"10196", "7429", "8542", "8229"},
},
},
{
name: "single value",
irq: "ERR",
want: Interrupt{
Info: "",
Devices: "",
Values: []string{"0"},
},
},
} {
t.Run(test.name, func(t *testing.T) {
if value, ok := interrupts[test.irq]; ok {
if value.Info != test.want.Info {
t.Errorf("info: want %s, have %s", test.want.Info, value.Info)
}
if value.Devices != test.want.Devices {
t.Errorf("devices: want %s, have %s", test.want.Devices, value.Devices)
}
if !reflect.DeepEqual(value.Values, test.want.Values) {
t.Errorf("values: want %v, have %v", test.want.Values, value.Values)
}
} else {
t.Errorf("IRQ %s not found", test.irq)
}
})
}
}
53 changes: 53 additions & 0 deletions testdata/fixtures.ttar
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,59 @@ flags: 02004002
mnt_id: 9
Mode: 400
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/proc/26231/interrupts
Lines: 49
CPU0 CPU1 CPU2 CPU3
0: 49 0 0 0 IO-APIC 2-edge timer
1: 0 0 0 9 IO-APIC 1-edge i8042
4: 0 1443 0 0 IO-APIC 4-edge ttyS0
8: 1 0 0 0 IO-APIC 8-edge rtc0
9: 0 0 0 0 IO-APIC 9-fasteoi acpi
12: 0 0 144 0 IO-APIC 12-edge i8042
22: 0 0 0 5 IO-APIC 22-fasteoi virtio1
24: 0 0 0 0 PCI-MSI 114688-edge virtio5-config
25: 1800 0 0 0 PCI-MSI 114689-edge virtio5-req.0
26: 0 1469 0 0 PCI-MSI 114690-edge virtio5-req.1
27: 0 0 2654 0 PCI-MSI 114691-edge virtio5-req.2
28: 0 0 0 1989 PCI-MSI 114692-edge virtio5-req.3
29: 1362 0 0 934 PCI-MSI 512000-edge ahci[0000:00:1f.2]
30: 0 0 0 0 PCI-MSI 98304-edge xhci_hcd
31: 0 0 0 0 PCI-MSI 98305-edge xhci_hcd
32: 0 0 0 0 PCI-MSI 98306-edge xhci_hcd
33: 0 0 0 0 PCI-MSI 98307-edge xhci_hcd
34: 0 0 0 0 PCI-MSI 98308-edge xhci_hcd
35: 0 0 0 0 PCI-MSI 16384-edge virtio0-config
36: 0 335 37 0 PCI-MSI 16385-edge virtio0-input.0
37: 0 0 0 318 PCI-MSI 16386-edge virtio0-output.0
38: 0 0 0 0 PCI-MSI 49152-edge virtio2-config
39: 1243 178 0 0 PCI-MSI 49153-edge virtio2-control
40: 0 0 0 0 PCI-MSI 49154-edge virtio2-cursor
41: 0 0 0 0 PCI-MSI 65536-edge virtio3-config
42: 0 0 0 0 PCI-MSI 65537-edge virtio3-virtqueues
43: 0 0 0 0 PCI-MSI 81920-edge virtio4-config
44: 0 0 0 0 PCI-MSI 81921-edge virtio4-virtqueues
NMI: 0 0 0 0 Non-maskable interrupts
LOC: 10196 7429 8542 8229 Local timer interrupts
SPU: 0 0 0 0 Spurious interrupts
PMI: 0 0 0 0 Performance monitoring interrupts
IWI: 0 3 11 6 IRQ work interrupts
RTR: 0 0 0 0 APIC ICR read retries
RES: 7997 11147 10898 12675 Rescheduling interrupts
CAL: 2761 2485 1787 2367 Function call interrupts
TLB: 212 137 158 231 TLB shootdowns
TRM: 0 0 0 0 Thermal event interrupts
THR: 0 0 0 0 Threshold APIC interrupts
DFR: 0 0 0 0 Deferred Error APIC interrupts
MCE: 0 0 0 0 Machine check exceptions
MCP: 1 1 1 1 Machine check polls
ERR: 0
MIS: 0

PIN: 0 0 0 0 Posted-interrupt notification event
NPI: 0 0 0 0 Nested posted-interrupt event
PIW: 0 0 0 0 Posted-interrupt wakeup event
Mode: 644
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/proc/26231/io
Lines: 7
rchar: 750339
Expand Down

0 comments on commit 42aaf8e

Please sign in to comment.