Skip to content

Commit 0d44b4a

Browse files
authored
refactor: extract types and parsing into subpackages (#9)
1 parent 52c5f20 commit 0d44b4a

File tree

6 files changed

+322
-214
lines changed

6 files changed

+322
-214
lines changed

cmd/lsirq/lsirq.go

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,26 @@
33
package main
44

55
import (
6-
"bufio"
76
"encoding/json"
87
"flag"
98
"fmt"
109
"log"
1110
"os"
12-
"regexp"
13-
"sort"
14-
"strconv"
15-
"strings"
11+
12+
"github.com/TimRots/gutil-linux/irq"
1613
)
1714

1815
const (
19-
PATH_PROC_INTERRUPTS = "/proc/interrupts"
20-
description = "Utility to display kernel interrupt information."
21-
usage = "Usage: lsirq [OPTIONS]\n\n%s\n\nOptions:\n"
16+
description = "Utility to display kernel interrupt information."
17+
usage = "Usage: lsirq [OPTIONS]\n\n%s\n\nOptions:\n"
2218
)
2319

2420
var (
25-
active_cpu_count int
26-
Interrupts []Interrupt
27-
irq string
28-
name []string
29-
noheadings = flag.Bool("noheadings", false, "don't print headings")
30-
pairs = flag.Bool("pairs", false, "use key=\"value\" output format")
31-
jsonoutput = flag.Bool("json", false, "use JSON output format")
21+
noheadings = flag.Bool("noheadings", false, "don't print headings")
22+
pairs = flag.Bool("pairs", false, "use key=\"value\" output format")
23+
jsonoutput = flag.Bool("json", false, "use JSON output format")
3224
)
3325

34-
type Interrupt struct {
35-
Irq string
36-
Total int
37-
Name string
38-
}
39-
4026
func init() {
4127
flag.BoolVar(noheadings, "n", false, "")
4228
flag.BoolVar(pairs, "p", false, "")
@@ -48,52 +34,12 @@ func init() {
4834
}
4935
}
5036

51-
func irqStat() []Interrupt {
52-
f, err := os.Open(PATH_PROC_INTERRUPTS)
37+
func PrintInterrupts(option string, ops ...bool) {
38+
irqStat, err := irq.IrqStat()
5339
if err != nil {
5440
log.Fatal(err)
5541
}
56-
defer f.Close()
57-
58-
scanner := bufio.NewScanner(f)
59-
for ln := 0; scanner.Scan(); ln++ {
60-
if ln == 0 {
61-
// First line shows the active cpus
62-
active_cpu_count = strings.Count(scanner.Text(), "CPU")
63-
continue
64-
}
65-
66-
// Match all non-whitespace character sequences
67-
i, total, words := 0, 0, regexp.MustCompile(`[\S]+`).FindAllString(scanner.Text(), -1)
68-
for _, word := range words {
69-
// As the lines are variadic in length we use active_cpu_count and
70-
// i count to determine how to parse the value based on the word its position.
71-
i++
72-
switch {
73-
case i == 1:
74-
irq = strings.ReplaceAll(string(word), ":", "")
75-
case i <= active_cpu_count+1:
76-
numint, _ := strconv.Atoi(word)
77-
total += numint
78-
case i > active_cpu_count+1:
79-
name = append(name, string(word))
80-
}
81-
}
82-
Interrupts = append(Interrupts, Interrupt{irq, total, strings.Join(name, " ")})
83-
irq, name = "", nil
84-
85-
}
86-
87-
// Sort slice by number
88-
sort.Slice(Interrupts, func(y, z int) bool {
89-
return Interrupts[y].Total > Interrupts[z].Total
90-
})
91-
92-
return Interrupts
93-
}
94-
95-
func PrintInterrupts(option string, ops ...bool) {
96-
irqStat, i := irqStat(), 0
42+
i := 0
9743
for _, interrupt := range irqStat {
9844
switch option {
9945
case "pairs":

cmd/lspci/lspci.go

Lines changed: 8 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77
package main
88

99
import (
10-
"bufio"
1110
"encoding/json"
1211
"encoding/xml"
1312
"flag"
1413
"fmt"
1514
"log"
1615
"os"
17-
"path/filepath"
18-
"regexp"
1916
"strings"
2017
"text/tabwriter"
18+
19+
"github.com/TimRots/gutil-linux/pci"
2120
)
2221

2322
const (
@@ -56,7 +55,6 @@ var (
5655
xmloutput = flag.Bool("xml", false, "use XML output format")
5756

5857
PciDevices []PciDevice
59-
errCnt = 0
6058
)
6159

6260
func init() {
@@ -88,7 +86,7 @@ func main() {
8886
}
8987

9088
func printDevices(opts string, params ...bool) {
91-
PciDevices := ParsePciDevices()
89+
PciDevices, err := pci.ParsePciDevices()
9290

9391
switch opts {
9492
case "json":
@@ -100,9 +98,13 @@ func printDevices(opts string, params ...bool) {
10098
default:
10199
tabWriter(PciDevices)
102100
}
101+
102+
if err != nil {
103+
log.Fatal(err)
104+
}
103105
}
104106

105-
func tabWriter(PciDevices []PciDevice) {
107+
func tabWriter(PciDevices []pci.PciDevice) {
106108
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
107109
defer w.Flush()
108110

@@ -157,147 +159,3 @@ func tabWriter(PciDevices []PciDevice) {
157159
}
158160
}
159161
}
160-
161-
func readFromFile(f string, w int) string {
162-
var value []string
163-
164-
file, err := os.Open(f)
165-
if err != nil {
166-
log.Fatal(err)
167-
}
168-
defer file.Close()
169-
170-
if w == 0 {
171-
w = 1
172-
}
173-
scanner, i, w := bufio.NewScanner(file), 0, w
174-
for scanner.Scan() {
175-
for _, word := range regexp.MustCompile(`[\S]+`).FindAllString(scanner.Text(), -1) {
176-
switch i++; {
177-
case i == w:
178-
value = append(value, string(word))
179-
}
180-
return strings.Join(value, " ")
181-
}
182-
}
183-
return ""
184-
}
185-
186-
func Lookup(searchType, ven, dev, class, subclass string) string {
187-
var found bool = false
188-
189-
// Open pci.ids
190-
f, err := os.Open("./pci.ids")
191-
if err != nil {
192-
errCnt++
193-
if errCnt < 2 {
194-
fmt.Println("Error: Cannot open pci.ids file, defaulting to numeric")
195-
}
196-
*numeric = true
197-
return ""
198-
}
199-
// close pci.ids file when done
200-
defer func() {
201-
if err := f.Close(); err != nil {
202-
log.Fatal(err)
203-
}
204-
}()
205-
206-
scanner := bufio.NewScanner(f)
207-
for scanner.Scan() {
208-
switch searchType {
209-
case "vendor": // Return first occurence of ven that does not have a \t prefix.
210-
if strings.Contains(scanner.Text(), ven) && !strings.HasPrefix(scanner.Text(), "\t") {
211-
return strings.TrimLeft(scanner.Text(), ven+" ")
212-
}
213-
214-
case "device": // Return first occurence of dev after vendor is found
215-
if strings.Contains(scanner.Text(), ven) && !strings.HasPrefix(scanner.Text(), "\t") {
216-
found = true
217-
}
218-
if strings.HasPrefix(scanner.Text(), "\t"+dev) && found {
219-
return strings.TrimLeft(scanner.Text(), "\t\t"+dev+" ")
220-
}
221-
222-
case "class": // Split class (eg: 0600), search for "C 06" and return first occurence of "\t\t00" therafter.
223-
if strings.Contains(scanner.Text(), "C"+" "+class[0:2]) && !strings.HasPrefix(scanner.Text(), "\t") {
224-
found = true
225-
}
226-
if strings.HasPrefix(scanner.Text(), "\t"+class[2:4]) && found {
227-
return strings.TrimLeft(scanner.Text(), "\t\t"+class+" ")
228-
229-
}
230-
case "subsystem": // Match and return line "\t\t vendor subsystem_device"
231-
if strings.Contains(scanner.Text(), ven+" "+subclass) {
232-
return strings.TrimLeft(scanner.Text(), "\t\t"+ven+" "+subclass)
233-
}
234-
}
235-
}
236-
if err := scanner.Err(); err != nil {
237-
log.Fatal(err)
238-
}
239-
return "Unknown " + searchType
240-
}
241-
242-
func ParsePciDevices() []PciDevice {
243-
var devices []string
244-
245-
read := func(bus, filename string) string {
246-
return readFromFile(filepath.Join(PATH_SYS_BUS_PCI_DEVICES, bus, filename), 1)
247-
}
248-
249-
lookupKernelDriver := func(bus string) string {
250-
read := readFromFile(filepath.Join(PATH_SYS_DEVICES_PCI+bus[0:7], bus, "uevent"), 1)
251-
if strings.Contains(read, "DRIVER=") {
252-
return read[7:]
253-
}
254-
return ""
255-
}
256-
257-
// Find all devices in /sys/bus/pci/devices/ and append each device to devices[]
258-
if err := filepath.Walk(
259-
PATH_SYS_BUS_PCI_DEVICES,
260-
func(path string, info os.FileInfo, err error) error {
261-
if !info.IsDir() {
262-
devices = append(devices, info.Name())
263-
}
264-
return nil
265-
}); err != nil {
266-
log.Fatal(err)
267-
}
268-
269-
// Iterate over each bus and parse & append values to PciDevices[]
270-
for _, bus := range devices {
271-
dev := read(bus, "device")[2:6]
272-
ven := read(bus, "vendor")[2:6]
273-
class := read(bus, "class")[2:6]
274-
subDev := read(bus, "subsystem_device")[2:6]
275-
subVen := read(bus, "subsystem_vendor")[2:6]
276-
mod := read(bus, "modalias")
277-
irq := read(bus, "irq")
278-
rev := read(bus, "revision")[2:4]
279-
280-
venName := Lookup("vendor", ven, "", "", "")
281-
devName := Lookup("device", ven, dev, "", "")
282-
devClass := Lookup("class", "", "", class, "")
283-
284-
subSys := ""
285-
if subVen != "0000" {
286-
subSys = Lookup("subsystem", ven, "", "", subDev)
287-
}
288-
289-
kernelDriver := lookupKernelDriver(bus)
290-
291-
if !*showdomainnumbers && !*jsonoutput {
292-
bus = strings.TrimPrefix(bus, "0000:")
293-
}
294-
295-
PciDevices = append(PciDevices,
296-
PciDevice{
297-
bus, ven, dev, class, subVen, subDev, irq, rev,
298-
venName, devName, devClass, subSys, "", mod, kernelDriver,
299-
},
300-
)
301-
}
302-
return PciDevices
303-
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/TimRots/gutil-linux
2+
3+
go 1.20

irq/irq.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package irq
2+
3+
import (
4+
"bufio"
5+
"os"
6+
"regexp"
7+
"sort"
8+
"strconv"
9+
"strings"
10+
)
11+
12+
const PATH_PROC_INTERRUPTS = "/proc/interrupts"
13+
14+
type Interrupt struct {
15+
Irq string
16+
Total int
17+
Name string
18+
}
19+
20+
func IrqStat() (Interrupts []Interrupt, err error) {
21+
var f *os.File
22+
if f, err = os.Open(PATH_PROC_INTERRUPTS); err != nil {
23+
return
24+
}
25+
defer f.Close()
26+
27+
var (
28+
active_cpu_count int
29+
irq string
30+
name []string
31+
)
32+
scanner := bufio.NewScanner(f)
33+
for ln := 0; scanner.Scan(); ln++ {
34+
if ln == 0 {
35+
// First line shows the active cpus
36+
active_cpu_count = strings.Count(scanner.Text(), "CPU")
37+
continue
38+
}
39+
40+
// Match all non-whitespace character sequences
41+
i, total, words := 0, 0, regexp.MustCompile(`[\S]+`).FindAllString(scanner.Text(), -1)
42+
for _, word := range words {
43+
// As the lines are variadic in length we use active_cpu_count and
44+
// i count to determine how to parse the value based on the word its position.
45+
i++
46+
switch {
47+
case i == 1:
48+
irq = strings.ReplaceAll(string(word), ":", "")
49+
case i <= active_cpu_count+1:
50+
numint, _ := strconv.Atoi(word)
51+
total += numint
52+
case i > active_cpu_count+1:
53+
name = append(name, string(word))
54+
}
55+
}
56+
Interrupts = append(Interrupts, Interrupt{irq, total, strings.Join(name, " ")})
57+
irq, name = "", nil
58+
}
59+
60+
// Sort slice by number
61+
sort.Slice(Interrupts, func(y, z int) bool {
62+
return Interrupts[y].Total > Interrupts[z].Total
63+
})
64+
65+
return
66+
}

0 commit comments

Comments
 (0)