Skip to content

Commit 42aaf8e

Browse files
authored
add /proc/interrupts (#475)
Signed-off-by: Furkan <[email protected]>
1 parent 88f86e5 commit 42aaf8e

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

proc_interrupts.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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("not enough fields in interrupts (expected at least 2 fields but got %d): %s", 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+
}

proc_interrupts_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
}

testdata/fixtures.ttar

+53
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,59 @@ flags: 02004002
8989
mnt_id: 9
9090
Mode: 400
9191
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
92+
Path: fixtures/proc/26231/interrupts
93+
Lines: 49
94+
CPU0 CPU1 CPU2 CPU3
95+
0: 49 0 0 0 IO-APIC 2-edge timer
96+
1: 0 0 0 9 IO-APIC 1-edge i8042
97+
4: 0 1443 0 0 IO-APIC 4-edge ttyS0
98+
8: 1 0 0 0 IO-APIC 8-edge rtc0
99+
9: 0 0 0 0 IO-APIC 9-fasteoi acpi
100+
12: 0 0 144 0 IO-APIC 12-edge i8042
101+
22: 0 0 0 5 IO-APIC 22-fasteoi virtio1
102+
24: 0 0 0 0 PCI-MSI 114688-edge virtio5-config
103+
25: 1800 0 0 0 PCI-MSI 114689-edge virtio5-req.0
104+
26: 0 1469 0 0 PCI-MSI 114690-edge virtio5-req.1
105+
27: 0 0 2654 0 PCI-MSI 114691-edge virtio5-req.2
106+
28: 0 0 0 1989 PCI-MSI 114692-edge virtio5-req.3
107+
29: 1362 0 0 934 PCI-MSI 512000-edge ahci[0000:00:1f.2]
108+
30: 0 0 0 0 PCI-MSI 98304-edge xhci_hcd
109+
31: 0 0 0 0 PCI-MSI 98305-edge xhci_hcd
110+
32: 0 0 0 0 PCI-MSI 98306-edge xhci_hcd
111+
33: 0 0 0 0 PCI-MSI 98307-edge xhci_hcd
112+
34: 0 0 0 0 PCI-MSI 98308-edge xhci_hcd
113+
35: 0 0 0 0 PCI-MSI 16384-edge virtio0-config
114+
36: 0 335 37 0 PCI-MSI 16385-edge virtio0-input.0
115+
37: 0 0 0 318 PCI-MSI 16386-edge virtio0-output.0
116+
38: 0 0 0 0 PCI-MSI 49152-edge virtio2-config
117+
39: 1243 178 0 0 PCI-MSI 49153-edge virtio2-control
118+
40: 0 0 0 0 PCI-MSI 49154-edge virtio2-cursor
119+
41: 0 0 0 0 PCI-MSI 65536-edge virtio3-config
120+
42: 0 0 0 0 PCI-MSI 65537-edge virtio3-virtqueues
121+
43: 0 0 0 0 PCI-MSI 81920-edge virtio4-config
122+
44: 0 0 0 0 PCI-MSI 81921-edge virtio4-virtqueues
123+
NMI: 0 0 0 0 Non-maskable interrupts
124+
LOC: 10196 7429 8542 8229 Local timer interrupts
125+
SPU: 0 0 0 0 Spurious interrupts
126+
PMI: 0 0 0 0 Performance monitoring interrupts
127+
IWI: 0 3 11 6 IRQ work interrupts
128+
RTR: 0 0 0 0 APIC ICR read retries
129+
RES: 7997 11147 10898 12675 Rescheduling interrupts
130+
CAL: 2761 2485 1787 2367 Function call interrupts
131+
TLB: 212 137 158 231 TLB shootdowns
132+
TRM: 0 0 0 0 Thermal event interrupts
133+
THR: 0 0 0 0 Threshold APIC interrupts
134+
DFR: 0 0 0 0 Deferred Error APIC interrupts
135+
MCE: 0 0 0 0 Machine check exceptions
136+
MCP: 1 1 1 1 Machine check polls
137+
ERR: 0
138+
MIS: 0
139+
140+
PIN: 0 0 0 0 Posted-interrupt notification event
141+
NPI: 0 0 0 0 Nested posted-interrupt event
142+
PIW: 0 0 0 0 Posted-interrupt wakeup event
143+
Mode: 644
144+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
92145
Path: fixtures/proc/26231/io
93146
Lines: 7
94147
rchar: 750339

0 commit comments

Comments
 (0)