@cryptotaxi247 / netdata-1 / commits / 4c987bbd0

go.d lmsensors improve performance (#18429)

Ilya Mashchenko committed Aug 28, 2024 at 14:43 UTC 4c987bbd06f5cfb4337ec6e68728af522baad9c7
2 files changed +111 -113
src/go/plugin/go.d/modules/sensors/lmsensors/scanner.go
+54 -56
@@ -2,7 +2,7 @@ package lmsensors
2
3 import (
4 "fmt"
5 - "io/ioutil"
5 + "io/fs"
6 "os"
7 "path/filepath"
8 "strings"
@@ -13,7 +13,7 @@ type filesystem interface {
13 ReadFile(filename string) (string, error)
14 Readlink(name string) (string, error)
15 Stat(name string) (os.FileInfo, error)
16 - Walk(root string, walkFn filepath.WalkFunc) error
16 + WalkDir(root string, walkFn fs.WalkDirFunc) error
17 }
18
19 // A Scanner scans for Devices, so data can be read from their Sensors.
@@ -30,30 +30,31 @@ func New() *Scanner {
30
31 // Scan scans for Devices and their Sensors.
32 func (s *Scanner) Scan() ([]*Device, error) {
33 - // Determine common device locations in Linux /sys filesystem.
33 paths, err := s.detectDevicePaths()
34 if err != nil {
35 return nil, err
36 }
37
38 var devices []*Device
40 - for _, p := range paths {
41 - d := &Device{}
42 - raw := make(map[string]map[string]string, 0)
39 +
40 + for _, rootPath := range paths {
41 + dev := &Device{}
42 + raw := make(map[string]map[string]string)
43
44 // Walk filesystem paths to fetch devices and sensors
45 - err := s.fs.Walk(p, func(path string, info os.FileInfo, err error) error {
45 + err := s.fs.WalkDir(rootPath, func(path string, de fs.DirEntry, err error) error {
46 if err != nil {
47 return err
48 }
49
50 - // Skip directories and anything that isn't a regular file
51 - if info.IsDir() || !info.Mode().IsRegular() {
50 + if de.IsDir() || !de.Type().IsRegular() {
51 + if de.IsDir() && path != rootPath {
52 + return fs.SkipDir
53 + }
54 return nil
55 }
56
55 - // Skip some files that can't be read or don't provide useful
56 - // sensor information
57 + // Skip some files that can't be read or don't provide useful sensor information
58 file := filepath.Base(path)
59 if shouldSkip(file) {
60 return nil
@@ -64,48 +65,46 @@ func (s *Scanner) Scan() ([]*Device, error) {
65 return nil
66 }
67
67 - switch file {
68 - // Found name of device
69 - case "name":
70 - d.Name = s
68 + if file == "name" {
69 + dev.Name = s
70 + return nil
71 }
72
73 // Sensor names in format "sensor#_foo", e.g. "temp1_input"
74 - fs := strings.SplitN(file, "_", 2)
75 - if len(fs) != 2 {
74 + parts := strings.SplitN(file, "_", 2)
75 + if len(parts) != 2 {
76 return nil
77 }
78
79 - // Gather sensor data into map for later processing
80 - if _, ok := raw[fs[0]]; !ok {
81 - raw[fs[0]] = make(map[string]string, 0)
79 + if _, ok := raw[parts[0]]; !ok {
80 + raw[parts[0]] = make(map[string]string)
81 }
82
84 - raw[fs[0]][fs[1]] = s
83 + raw[parts[0]][parts[1]] = s
84 +
85 return nil
86 })
87 if err != nil {
88 return nil, err
89 }
90
91 - // Parse all possible sensors from raw data
91 sensors, err := parseSensors(raw)
92 if err != nil {
93 return nil, err
94 }
95
97 - d.Sensors = sensors
98 - devices = append(devices, d)
96 + dev.Sensors = sensors
97 + devices = append(devices, dev)
98 }
99
100 renameDevices(devices)
101 +
102 return devices, nil
103 }
104
105 -// renameDevices renames devices in place to prevent duplicate device names,
106 -// and to number each device.
105 +// renameDevices renames devices in place to prevent duplicate device names, and to number each device.
106 func renameDevices(devices []*Device) {
108 - nameCount := make(map[string]int, 0)
107 + nameCount := make(map[string]int)
108
109 for i := range devices {
110 name := devices[i].Name
@@ -117,19 +116,17 @@ func renameDevices(devices []*Device) {
116 }
117 }
118
120 -// detectDevicePaths performs a filesystem walk to paths where devices may
121 -// reside on Linux.
119 +// detectDevicePaths performs a filesystem walk to paths where devices may reside on Linux.
120 func (s *Scanner) detectDevicePaths() ([]string, error) {
121 const lookPath = "/sys/class/hwmon"
122
123 var paths []string
126 - err := s.fs.Walk(lookPath, func(path string, info os.FileInfo, err error) error {
124 + err := s.fs.WalkDir(lookPath, func(path string, de os.DirEntry, err error) error {
125 if err != nil {
126 return err
127 }
128
131 - // Skip anything that isn't a symlink
132 - if info.Mode()&os.ModeSymlink == 0 {
129 + if de.Type()&os.ModeSymlink == 0 {
130 return nil
131 }
132
@@ -137,10 +134,10 @@ func (s *Scanner) detectDevicePaths() ([]string, error) {
134 if err != nil {
135 return err
136 }
137 +
138 dest = filepath.Join(lookPath, filepath.Clean(dest))
139
142 - // Symlink destination has a file called name, meaning a sensor exists
143 - // here and data can be retrieved
140 + // Symlink destination has a file called name, meaning a sensor exists here and data can be retrieved
141 fi, err := s.fs.Stat(filepath.Join(dest, "name"))
142 if err != nil && !os.IsNotExist(err) {
143 return err
@@ -150,16 +147,14 @@ func (s *Scanner) detectDevicePaths() ([]string, error) {
147 return nil
148 }
149
153 - // Symlink destination has another symlink called device, which can be
154 - // read and used to retrieve data
150 + // Symlink destination has another symlink called device, which can be read and used to retrieve data
151 device := filepath.Join(dest, "device")
152 fi, err = s.fs.Stat(device)
153 if err != nil {
158 - if os.IsNotExist(err) {
159 - return nil
154 + if !os.IsNotExist(err) {
155 + return err
156 }
161 -
162 - return err
157 + return nil
158 }
159
160 if fi.Mode()&os.ModeSymlink != 0 {
@@ -170,27 +165,26 @@ func (s *Scanner) detectDevicePaths() ([]string, error) {
165 if err != nil {
166 return err
167 }
168 +
169 dest = filepath.Join(dest, filepath.Clean(device))
170
175 - // Symlink destination has a file called name, meaning a sensor exists
176 - // here and data can be retrieved
171 + // Symlink destination has a file called name, meaning a sensor exists here and data can be retrieved
172 if _, err := s.fs.Stat(filepath.Join(dest, "name")); err != nil {
178 - if os.IsNotExist(err) {
179 - return nil
173 + if !os.IsNotExist(err) {
174 + return err
175 }
181 -
182 - return err
176 + return nil
177 }
178
179 paths = append(paths, dest)
180 +
181 return nil
182 })
183
184 return paths, err
185 }
186
192 -// shouldSkip indicates if a given filename should be skipped during the
193 -// filesystem walk operation.
187 +// shouldSkip indicates if a given filename should be skipped during the filesystem walk operation.
188 func shouldSkip(file string) bool {
189 if strings.HasPrefix(file, "runtime_") {
190 return true
@@ -212,21 +206,25 @@ func shouldSkip(file string) bool {
206
207 var _ filesystem = &systemFilesystem{}
208
215 -// A systemFilesystem is a filesystem which uses operations on the host
216 -// filesystem.
209 +// A systemFilesystem is a filesystem which uses operations on the host filesystem.
210 type systemFilesystem struct{}
211
212 func (fs *systemFilesystem) ReadFile(filename string) (string, error) {
220 - b, err := ioutil.ReadFile(filename)
213 + b, err := os.ReadFile(filename)
214 if err != nil {
215 return "", err
216 }
224 -
217 return strings.TrimSpace(string(b)), nil
218 }
219
228 -func (fs *systemFilesystem) Readlink(name string) (string, error) { return os.Readlink(name) }
229 -func (fs *systemFilesystem) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
230 -func (fs *systemFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
231 - return filepath.Walk(root, walkFn)
220 +func (fs *systemFilesystem) Readlink(name string) (string, error) {
221 + return os.Readlink(name)
222 +}
223 +
224 +func (fs *systemFilesystem) Stat(name string) (os.FileInfo, error) {
225 + return os.Stat(name)
226 +}
227 +
228 +func (fs *systemFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
229 + return filepath.WalkDir(root, walkFn)
230 }
src/go/plugin/go.d/modules/sensors/lmsensors/scanner_test.go
+57 -57
@@ -2,8 +2,8 @@ package lmsensors
2
3 import (
4 "fmt"
5 + "io/fs"
6 "os"
6 - "path/filepath"
7 "reflect"
8 "strings"
9 "testing"
@@ -30,19 +30,19 @@ func TestScannerScan(t *testing.T) {
30 files: []memoryFile{
31 {
32 name: "/sys/class/hwmon",
33 - info: &memoryFileInfo{
33 + dirEntry: &memoryDirEntry{
34 isDir: true,
35 },
36 },
37 {
38 name: "/sys/class/hwmon/hwmon0",
39 - info: &memoryFileInfo{
39 + dirEntry: &memoryDirEntry{
40 mode: os.ModeSymlink,
41 },
42 },
43 {
44 name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00",
45 - info: &memoryFileInfo{
45 + dirEntry: &memoryDirEntry{
46 isDir: true,
47 },
48 },
@@ -51,8 +51,8 @@ func TestScannerScan(t *testing.T) {
51 err: os.ErrNotExist,
52 },
53 {
54 - name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device",
55 - info: &memoryFileInfo{
54 + name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device",
55 + dirEntry: &memoryDirEntry{
56 // mode: os.ModeSymlink,
57 },
58 },
@@ -110,19 +110,19 @@ func TestScannerScan(t *testing.T) {
110 files: []memoryFile{
111 {
112 name: "/sys/class/hwmon",
113 - info: &memoryFileInfo{
113 + dirEntry: &memoryDirEntry{
114 isDir: true,
115 },
116 },
117 {
118 name: "/sys/class/hwmon/hwmon0",
119 - info: &memoryFileInfo{
119 + dirEntry: &memoryDirEntry{
120 mode: os.ModeSymlink,
121 },
122 },
123 {
124 name: "/sys/devices/virtual/hwmon/hwmon0",
125 - info: &memoryFileInfo{
125 + dirEntry: &memoryDirEntry{
126 isDir: true,
127 },
128 },
@@ -162,19 +162,19 @@ func TestScannerScan(t *testing.T) {
162 files: []memoryFile{
163 {
164 name: "/sys/class/hwmon",
165 - info: &memoryFileInfo{
165 + dirEntry: &memoryDirEntry{
166 isDir: true,
167 },
168 },
169 {
170 name: "/sys/class/hwmon/hwmon1",
171 - info: &memoryFileInfo{
171 + dirEntry: &memoryDirEntry{
172 mode: os.ModeSymlink,
173 },
174 },
175 {
176 name: "/sys/devices/platform/coretemp.0",
177 - info: &memoryFileInfo{
177 + dirEntry: &memoryDirEntry{
178 isDir: true,
179 },
180 },
@@ -183,8 +183,8 @@ func TestScannerScan(t *testing.T) {
183 err: os.ErrNotExist,
184 },
185 {
186 - name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
187 - info: &memoryFileInfo{
186 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
187 + dirEntry: &memoryDirEntry{
188 // mode: os.ModeSymlink,
189 },
190 },
@@ -266,19 +266,19 @@ func TestScannerScan(t *testing.T) {
266 files: []memoryFile{
267 {
268 name: "/sys/class/hwmon",
269 - info: &memoryFileInfo{
269 + dirEntry: &memoryDirEntry{
270 isDir: true,
271 },
272 },
273 {
274 name: "/sys/class/hwmon/hwmon2",
275 - info: &memoryFileInfo{
275 + dirEntry: &memoryDirEntry{
276 mode: os.ModeSymlink,
277 },
278 },
279 {
280 name: "/sys/devices/platform/it87.2608",
281 - info: &memoryFileInfo{
281 + dirEntry: &memoryDirEntry{
282 isDir: true,
283 },
284 },
@@ -287,8 +287,8 @@ func TestScannerScan(t *testing.T) {
287 err: os.ErrNotExist,
288 },
289 {
290 - name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/device",
291 - info: &memoryFileInfo{
290 + name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/device",
291 + dirEntry: &memoryDirEntry{
292 // mode: os.ModeSymlink,
293 },
294 },
@@ -426,25 +426,25 @@ func TestScannerScan(t *testing.T) {
426 files: []memoryFile{
427 {
428 name: "/sys/class/hwmon",
429 - info: &memoryFileInfo{
429 + dirEntry: &memoryDirEntry{
430 isDir: true,
431 },
432 },
433 {
434 name: "/sys/class/hwmon/hwmon1",
435 - info: &memoryFileInfo{
435 + dirEntry: &memoryDirEntry{
436 mode: os.ModeSymlink,
437 },
438 },
439 {
440 name: "/sys/class/hwmon/hwmon2",
441 - info: &memoryFileInfo{
441 + dirEntry: &memoryDirEntry{
442 mode: os.ModeSymlink,
443 },
444 },
445 {
446 name: "/sys/devices/platform/coretemp.0",
447 - info: &memoryFileInfo{
447 + dirEntry: &memoryDirEntry{
448 isDir: true,
449 },
450 },
@@ -453,8 +453,8 @@ func TestScannerScan(t *testing.T) {
453 err: os.ErrNotExist,
454 },
455 {
456 - name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
457 - info: &memoryFileInfo{
456 + name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
457 + dirEntry: &memoryDirEntry{
458 // mode: os.ModeSymlink,
459 },
460 },
@@ -504,7 +504,7 @@ func TestScannerScan(t *testing.T) {
504 },
505 {
506 name: "/sys/devices/platform/coretemp.1",
507 - info: &memoryFileInfo{
507 + dirEntry: &memoryDirEntry{
508 isDir: true,
509 },
510 },
@@ -513,8 +513,8 @@ func TestScannerScan(t *testing.T) {
513 err: os.ErrNotExist,
514 },
515 {
516 - name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device",
517 - info: &memoryFileInfo{
516 + name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device",
517 + dirEntry: &memoryDirEntry{
518 // mode: os.ModeSymlink,
519 },
520 },
@@ -619,19 +619,19 @@ func TestScannerScan(t *testing.T) {
619 files: []memoryFile{
620 {
621 name: "/sys/class/hwmon",
622 - info: &memoryFileInfo{
622 + dirEntry: &memoryDirEntry{
623 isDir: true,
624 },
625 },
626 {
627 name: "/sys/class/hwmon/hwmon0",
628 - info: &memoryFileInfo{
628 + dirEntry: &memoryDirEntry{
629 mode: os.ModeSymlink,
630 },
631 },
632 {
633 name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0",
634 - info: &memoryFileInfo{
634 + dirEntry: &memoryDirEntry{
635 isDir: true,
636 },
637 },
@@ -640,8 +640,8 @@ func TestScannerScan(t *testing.T) {
640 err: os.ErrNotExist,
641 },
642 {
643 - name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device",
644 - info: &memoryFileInfo{
643 + name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device",
644 + dirEntry: &memoryDirEntry{
645 // mode: os.ModeSymlink,
646 },
647 },
@@ -747,11 +747,11 @@ func (fs *memoryFilesystem) Readlink(name string) (string, error) {
747 func (fs *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
748 for _, f := range fs.files {
749 if f.name == name {
750 - info := f.info
751 - if info == nil {
752 - info = &memoryFileInfo{}
750 + de := f.dirEntry
751 + if de == nil {
752 + de = &memoryDirEntry{}
753 }
754 -
754 + info, _ := de.Info()
755 return info, f.err
756 }
757 }
@@ -759,7 +759,7 @@ func (fs *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
759 return nil, fmt.Errorf("stat: file %q not in memory", name)
760 }
761
762 -func (fs *memoryFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
762 +func (fs *memoryFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
763 if _, err := fs.Stat(root); err != nil {
764 return err
765 }
@@ -770,12 +770,12 @@ func (fs *memoryFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
770 continue
771 }
772
773 - info := f.info
774 - if info == nil {
775 - info = &memoryFileInfo{}
773 + de := f.dirEntry
774 + if de == nil {
775 + de = &memoryDirEntry{}
776 }
777
778 - if err := walkFn(f.name, info, nil); err != nil {
778 + if err := walkFn(f.name, de, nil); err != nil {
779 return err
780 }
781 }
@@ -787,24 +787,24 @@ func (fs *memoryFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
787 type memoryFile struct {
788 name string
789 contents string
790 - info os.FileInfo
790 + dirEntry fs.DirEntry
791 err error
792 }
793
794 -var _ os.FileInfo = &memoryFileInfo{}
794 +var _ fs.DirEntry = &memoryDirEntry{}
795
796 -// A memoryFileInfo is an os.FileInfo used by memoryFiles.
797 -type memoryFileInfo struct {
798 - name string
799 - size int64
800 - mode os.FileMode
801 - modTime time.Time
802 - isDir bool
796 +// A memoryDirEntry is a fs.DirEntry used by memoryFiles.
797 +type memoryDirEntry struct {
798 + name string
799 + mode os.FileMode
800 + isDir bool
801 }
802
805 -func (fi *memoryFileInfo) Name() string { return fi.name }
806 -func (fi *memoryFileInfo) Size() int64 { return fi.size }
807 -func (fi *memoryFileInfo) Mode() os.FileMode { return fi.mode }
808 -func (fi *memoryFileInfo) ModTime() time.Time { return fi.modTime }
809 -func (fi *memoryFileInfo) IsDir() bool { return fi.isDir }
810 -func (fi *memoryFileInfo) Sys() interface{} { return nil }
803 +func (fi *memoryDirEntry) Name() string { return fi.name }
804 +func (fi *memoryDirEntry) Type() os.FileMode { return fi.mode }
805 +func (fi *memoryDirEntry) IsDir() bool { return fi.isDir }
806 +func (fi *memoryDirEntry) Info() (fs.FileInfo, error) { return fi, nil }
807 +func (fi *memoryDirEntry) Sys() interface{} { return nil }
808 +func (fi *memoryDirEntry) Size() int64 { return 0 }
809 +func (fi *memoryDirEntry) Mode() os.FileMode { return fi.Type() }
810 +func (fi *memoryDirEntry) ModTime() time.Time { return time.Now() }