proc_interrupts.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. package procfs
  14. import (
  15. "bufio"
  16. "bytes"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "strconv"
  21. "strings"
  22. "github.com/prometheus/procfs/internal/util"
  23. )
  24. // Interrupt represents a single interrupt line.
  25. type Interrupt struct {
  26. // Info is the type of interrupt.
  27. Info string
  28. // Devices is the name of the device that is located at that IRQ
  29. Devices string
  30. // Values is the number of interrupts per CPU.
  31. Values []string
  32. }
  33. // Interrupts models the content of /proc/interrupts. Key is the IRQ number.
  34. // - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-interrupts
  35. // - https://raspberrypi.stackexchange.com/questions/105802/explanation-of-proc-interrupts-output
  36. type Interrupts map[string]Interrupt
  37. // Interrupts creates a new instance from a given Proc instance.
  38. func (p Proc) Interrupts() (Interrupts, error) {
  39. data, err := util.ReadFileNoStat(p.path("interrupts"))
  40. if err != nil {
  41. return nil, err
  42. }
  43. return parseInterrupts(bytes.NewReader(data))
  44. }
  45. func parseInterrupts(r io.Reader) (Interrupts, error) {
  46. var (
  47. interrupts = Interrupts{}
  48. scanner = bufio.NewScanner(r)
  49. )
  50. if !scanner.Scan() {
  51. return nil, errors.New("interrupts empty")
  52. }
  53. cpuNum := len(strings.Fields(scanner.Text())) // one header per cpu
  54. for scanner.Scan() {
  55. parts := strings.Fields(scanner.Text())
  56. if len(parts) == 0 { // skip empty lines
  57. continue
  58. }
  59. if len(parts) < 2 {
  60. return nil, fmt.Errorf("%w: Not enough fields in interrupts (expected 2+ fields but got %d): %s", ErrFileParse, len(parts), parts)
  61. }
  62. intName := parts[0][:len(parts[0])-1] // remove trailing :
  63. if len(parts) == 2 {
  64. interrupts[intName] = Interrupt{
  65. Info: "",
  66. Devices: "",
  67. Values: []string{
  68. parts[1],
  69. },
  70. }
  71. continue
  72. }
  73. intr := Interrupt{
  74. Values: parts[1 : cpuNum+1],
  75. }
  76. if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt
  77. intr.Info = parts[cpuNum+1]
  78. intr.Devices = strings.Join(parts[cpuNum+2:], " ")
  79. } else {
  80. intr.Info = strings.Join(parts[cpuNum+1:], " ")
  81. }
  82. interrupts[intName] = intr
  83. }
  84. return interrupts, scanner.Err()
  85. }