add intel_gpu_top collector (#17344)
Ilya Mashchenko committed
Apr 9, 2024 at 13:45 UTC
3acc67a1c7dea77a7e2160c705ef00ab0294cb0d
15 files changed
+887
src/go/collectors/go.d.plugin/README.md
+1
@@ -77,6 +77,7 @@ see the appropriate collector readme.
77
| [haproxy](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/haproxy) | HAProxy |
78
| [hdfs](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/hdfs) | HDFS |
79
| [httpcheck](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/httpcheck) | Any HTTP Endpoint |
80
+| [intelgpu](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/intelgpu) | Intel integrated GPU |
81
| [isc_dhcpd](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/isc_dhcpd) | ISC DHCP |
82
| [k8s_kubelet](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/k8s_kubelet) | Kubelet |
83
| [k8s_kubeproxy](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/k8s_kubeproxy) | Kube-proxy |
src/go/collectors/go.d.plugin/config/go.d.conf
+1
@@ -40,6 +40,7 @@ modules:
40
# haproxy: yes
41
# hdfs: yes
42
# httpcheck: yes
43
+# intelgpu: yes
44
# isc_dhcpd: yes
45
# k8s_kubelet: yes
46
# k8s_kubeproxy: yes
src/go/collectors/go.d.plugin/config/go.d/intelgpu.conf
new
+6
@@ -0,0 +1,6 @@
1
+## All available configuration options, their descriptions and default values:
2
+## https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/intelgpu#readme
3
+
4
+#jobs:
5
+# - name: intelgpu
6
+# binary_path: /usr/bin/intel_gpu_top
src/go/collectors/go.d.plugin/modules/init.go
+1
@@ -30,6 +30,7 @@ import (
30
_ "github.com/netdata/netdata/go/go.d.plugin/modules/haproxy"
31
_ "github.com/netdata/netdata/go/go.d.plugin/modules/hdfs"
32
_ "github.com/netdata/netdata/go/go.d.plugin/modules/httpcheck"
33
+ _ "github.com/netdata/netdata/go/go.d.plugin/modules/intelgpu"
34
_ "github.com/netdata/netdata/go/go.d.plugin/modules/isc_dhcpd"
35
_ "github.com/netdata/netdata/go/go.d.plugin/modules/k8s_kubelet"
36
_ "github.com/netdata/netdata/go/go.d.plugin/modules/k8s_kubeproxy"
src/go/collectors/go.d.plugin/modules/intelgpu/charts.go
new
+92
@@ -0,0 +1,92 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ "fmt"
7
+ "strings"
8
+
9
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
10
+)
11
+
12
+const (
13
+ prioGPUFrequency = module.Priority + iota
14
+ prioGPUPower
15
+ prioGPUEngineBusy
16
+)
17
+
18
+var charts = module.Charts{
19
+ intelGPUFrequencyChart.Copy(),
20
+ intelGPUPowerGPUChart.Copy(),
21
+}
22
+
23
+var intelGPUFrequencyChart = module.Chart{
24
+ ID: "igpu_frequency",
25
+ Title: "Intel GPU frequency",
26
+ Units: "MHz",
27
+ Fam: "frequency",
28
+ Ctx: "intelgpu.frequency",
29
+ Type: module.Line,
30
+ Priority: prioGPUFrequency,
31
+ Dims: module.Dims{
32
+ {ID: "frequency_actual", Name: "frequency", Div: precision},
33
+ },
34
+}
35
+
36
+var intelGPUPowerGPUChart = module.Chart{
37
+ ID: "igpu_power_gpu",
38
+ Title: "Intel GPU power",
39
+ Units: "Watts",
40
+ Fam: "power",
41
+ Ctx: "intelgpu.power",
42
+ Type: module.Line,
43
+ Priority: prioGPUPower,
44
+ Dims: module.Dims{
45
+ {ID: "power_gpu", Name: "gpu", Div: precision},
46
+ {ID: "power_package", Name: "package", Div: precision},
47
+ },
48
+}
49
+
50
+var intelGPUEngineBusyPercChartTmpl = module.Chart{
51
+ ID: "igpu_engine_%s_busy_percentage",
52
+ Title: "Intel GPU engine busy time percentage",
53
+ Units: "percentage",
54
+ Fam: "engines",
55
+ Ctx: "intelgpu.engine_busy_perc",
56
+ Type: module.Line,
57
+ Priority: prioGPUEngineBusy,
58
+ Dims: module.Dims{
59
+ {ID: "engine_%s_busy", Name: "busy", Div: precision},
60
+ },
61
+}
62
+
63
+func (ig *IntelGPU) addEngineCharts(engine string) {
64
+ chart := intelGPUEngineBusyPercChartTmpl.Copy()
65
+
66
+ s := strings.ToLower(engine)
67
+ s = strings.ReplaceAll(s, "/", "_")
68
+
69
+ chart.ID = fmt.Sprintf(chart.ID, s)
70
+ chart.Labels = []module.Label{
71
+ {Key: "engine", Value: engineDisplayName(engine)},
72
+ {Key: "engine_instance", Value: engine},
73
+ }
74
+ for _, dim := range chart.Dims {
75
+ dim.ID = fmt.Sprintf(dim.ID, engine)
76
+ }
77
+
78
+ if err := ig.Charts().Add(chart); err != nil {
79
+ ig.Warning(err)
80
+ }
81
+}
82
+
83
+func engineDisplayName(engine string) string {
84
+ // https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tools/intel_gpu_top.c#L431
85
+ engines := []string{"Render/3D", "Blitter", "VideoEnhance", "Video", "Compute"}
86
+ for _, name := range engines {
87
+ if strings.HasPrefix(engine, name) {
88
+ return name
89
+ }
90
+ }
91
+ return "unknown"
92
+}
src/go/collectors/go.d.plugin/modules/intelgpu/collect.go
new
+76
@@ -0,0 +1,76 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ "encoding/json"
7
+ "errors"
8
+ "fmt"
9
+)
10
+
11
+type (
12
+ gpuSummaryStats struct {
13
+ Frequency struct {
14
+ Actual float64 `json:"actual"`
15
+ } `json:"frequency"`
16
+ Power struct {
17
+ GPU float64 `json:"gpu"`
18
+ Package float64 `json:"package"`
19
+ } `json:"power"`
20
+ Engines map[string]struct {
21
+ Busy float64 `json:"busy"`
22
+ } `json:"engines"`
23
+ }
24
+)
25
+
26
+const precision = 100
27
+
28
+func (ig *IntelGPU) collect() (map[string]int64, error) {
29
+ if ig.exec == nil {
30
+ return nil, errors.New("collector not initialized")
31
+ }
32
+
33
+ stats, err := ig.getGPUSummaryStats()
34
+ if err != nil {
35
+ return nil, err
36
+ }
37
+
38
+ mx := make(map[string]int64)
39
+
40
+ mx["frequency_actual"] = int64(stats.Frequency.Actual * precision)
41
+ mx["power_gpu"] = int64(stats.Power.GPU * precision)
42
+ mx["power_package"] = int64(stats.Power.Package * precision)
43
+
44
+ for name, es := range stats.Engines {
45
+ if !ig.engines[name] {
46
+ ig.addEngineCharts(name)
47
+ ig.engines[name] = true
48
+ }
49
+
50
+ key := fmt.Sprintf("engine_%s_busy", name)
51
+ mx[key] = int64(es.Busy * precision)
52
+ }
53
+
54
+ return mx, nil
55
+}
56
+func (ig *IntelGPU) getGPUSummaryStats() (*gpuSummaryStats, error) {
57
+ bs, err := ig.exec.queryGPUSummaryJson()
58
+ if err != nil {
59
+ return nil, err
60
+ }
61
+
62
+ if len(bs) == 0 {
63
+ return nil, errors.New("query returned empty response")
64
+ }
65
+
66
+ var stats gpuSummaryStats
67
+ if err := json.Unmarshal(bs, &stats); err != nil {
68
+ return nil, err
69
+ }
70
+
71
+ if len(stats.Engines) == 0 {
72
+ return nil, errors.New("query returned unexpected response")
73
+ }
74
+
75
+ return &stats, nil
76
+}
src/go/collectors/go.d.plugin/modules/intelgpu/config_schema.json
new
+37
@@ -0,0 +1,37 @@
1
+{
2
+ "jsonSchema": {
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "title": "Intel GPU collector configuration.",
5
+ "type": "object",
6
+ "properties": {
7
+ "update_every": {
8
+ "title": "Update every",
9
+ "description": "Data collection interval, measured in seconds.",
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "default": 1
13
+ },
14
+ "binary_path": {
15
+ "title": "Binary path",
16
+ "description": "Path to the `intel_gpu_top` binary.",
17
+ "type": "string",
18
+ "default": "/usr/bin/intel_gpu_top"
19
+ }
20
+ },
21
+ "required": [
22
+ "binary_path"
23
+ ],
24
+ "additionalProperties": false,
25
+ "patternProperties": {
26
+ "^name$": {}
27
+ }
28
+ },
29
+ "uiSchema": {
30
+ "uiOptions": {
31
+ "fullPage": true
32
+ },
33
+ "binary_path": {
34
+ "ui:help": "If an absolute path is provided, the collector will use it directly; otherwise, it will search for the binary in directories specified in the PATH environment variable."
35
+ }
36
+ }
37
+}
src/go/collectors/go.d.plugin/modules/intelgpu/exec.go
new
+140
@@ -0,0 +1,140 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ "bufio"
7
+ "bytes"
8
+ "errors"
9
+ "os/exec"
10
+ "strconv"
11
+ "sync"
12
+ "time"
13
+)
14
+
15
+func newIntelGpuTopExec(binPath string, updateEvery int) (*intelGpuTopExec, error) {
16
+ topExec := &intelGpuTopExec{
17
+ binPath: binPath,
18
+ updateEvery: updateEvery,
19
+ }
20
+
21
+ if err := topExec.run(); err != nil {
22
+ return nil, err
23
+ }
24
+
25
+ return topExec, nil
26
+}
27
+
28
+type intelGpuTopExec struct {
29
+ binPath string
30
+ updateEvery int
31
+
32
+ cmd *exec.Cmd
33
+ done chan struct{}
34
+
35
+ mux sync.Mutex
36
+ lastSample string
37
+}
38
+
39
+func (e *intelGpuTopExec) run() error {
40
+ refresh := 900
41
+ if e.updateEvery > 1 {
42
+ refresh = e.updateEvery*1000 - 500 // milliseconds
43
+ }
44
+
45
+ cmd := exec.Command(e.binPath, "-J", "-s", strconv.Itoa(refresh))
46
+
47
+ r, err := cmd.StdoutPipe()
48
+ if err != nil {
49
+ return err
50
+ }
51
+
52
+ if err := cmd.Start(); err != nil {
53
+ return err
54
+ }
55
+
56
+ firstSample := make(chan struct{}, 1)
57
+ done := make(chan struct{})
58
+ e.cmd = cmd
59
+ e.done = done
60
+
61
+ go func() {
62
+ defer close(done)
63
+ sc := bufio.NewScanner(r)
64
+ var buf bytes.Buffer
65
+ var n int
66
+
67
+ for sc.Scan() {
68
+ if n++; n > 1000 {
69
+ break
70
+ }
71
+
72
+ text := sc.Text()
73
+
74
+ if buf.Cap() == 0 && text != "{" || text == "" {
75
+ continue
76
+ }
77
+
78
+ if text == "}," {
79
+ text = "}"
80
+ }
81
+
82
+ buf.WriteString(text + "\n")
83
+
84
+ if text[0] == '}' {
85
+ e.mux.Lock()
86
+ e.lastSample = buf.String()
87
+ e.mux.Unlock()
88
+
89
+ select {
90
+ case firstSample <- struct{}{}:
91
+ default:
92
+ }
93
+
94
+ buf.Reset()
95
+ n = 0
96
+ }
97
+ }
98
+ }()
99
+
100
+ select {
101
+ case <-e.done:
102
+ _ = e.stop()
103
+ return errors.New("process exited before the first sample was collected")
104
+ case <-time.After(time.Second * 3):
105
+ _ = e.stop()
106
+ return errors.New("timed out waiting for first sample")
107
+ case <-firstSample:
108
+ return nil
109
+ }
110
+}
111
+
112
+func (e *intelGpuTopExec) queryGPUSummaryJson() ([]byte, error) {
113
+ select {
114
+ case <-e.done:
115
+ return nil, errors.New("process has already exited")
116
+ default:
117
+ }
118
+
119
+ e.mux.Lock()
120
+ defer e.mux.Unlock()
121
+
122
+ return []byte(e.lastSample), nil
123
+}
124
+
125
+func (e *intelGpuTopExec) stop() error {
126
+ if e.cmd == nil || e.cmd.Process == nil {
127
+ return nil
128
+ }
129
+
130
+ _ = e.cmd.Process.Kill()
131
+ _, _ = e.cmd.Process.Wait()
132
+ e.cmd = nil
133
+
134
+ select {
135
+ case <-e.done:
136
+ return nil
137
+ case <-time.After(time.Second * 2):
138
+ return errors.New("timed out waiting for process to exit")
139
+ }
140
+}
src/go/collectors/go.d.plugin/modules/intelgpu/init.go
new
+22
@@ -0,0 +1,22 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ "fmt"
7
+ "os"
8
+ "os/exec"
9
+)
10
+
11
+func (ig *IntelGPU) initIntelGPUTopExec() (intelGpuTop, error) {
12
+ binPath := ig.BinaryPath
13
+ if _, err := os.Stat(binPath); os.IsNotExist(err) {
14
+ path, err := exec.LookPath(ig.binName)
15
+ if err != nil {
16
+ return nil, fmt.Errorf("error on lookup '%s': %v", ig.binName, err)
17
+ }
18
+ binPath = path
19
+ }
20
+
21
+ return newIntelGpuTopExec(binPath, ig.UpdateEvery)
22
+}
src/go/collectors/go.d.plugin/modules/intelgpu/intelgpu.go
new
+110
@@ -0,0 +1,110 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ _ "embed"
7
+ "errors"
8
+
9
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
10
+)
11
+
12
+//go:embed "config_schema.json"
13
+var configSchema string
14
+
15
+func init() {
16
+ module.Register("intelgpu", module.Creator{
17
+ Create: func() module.Module { return New() },
18
+ JobConfigSchema: configSchema,
19
+ })
20
+}
21
+
22
+func New() *IntelGPU {
23
+ return &IntelGPU{
24
+ Config: Config{
25
+ BinaryPath: "/usr/bin/intel_gpu_top",
26
+ },
27
+ binName: "intel_gpu_top",
28
+ charts: charts.Copy(),
29
+ engines: make(map[string]bool),
30
+ }
31
+}
32
+
33
+type Config struct {
34
+ UpdateEvery int `yaml:"update_every" json:"update_every"`
35
+ BinaryPath string `yaml:"binary_path" json:"binary_path"`
36
+}
37
+
38
+type (
39
+ IntelGPU struct {
40
+ module.Base
41
+ Config `yaml:",inline" json:""`
42
+
43
+ charts *module.Charts
44
+
45
+ exec intelGpuTop
46
+ binName string
47
+
48
+ engines map[string]bool
49
+ }
50
+ intelGpuTop interface {
51
+ queryGPUSummaryJson() ([]byte, error)
52
+ stop() error
53
+ }
54
+)
55
+
56
+func (ig *IntelGPU) Configuration() any {
57
+ return ig.Config
58
+}
59
+
60
+func (ig *IntelGPU) Init() error {
61
+ topExec, err := ig.initIntelGPUTopExec()
62
+ if err != nil {
63
+ ig.Error(err)
64
+ return err
65
+ }
66
+
67
+ ig.exec = topExec
68
+
69
+ return nil
70
+}
71
+
72
+func (ig *IntelGPU) Check() error {
73
+ mx, err := ig.collect()
74
+ if err != nil {
75
+ ig.Error(err)
76
+ return err
77
+ }
78
+
79
+ if len(mx) == 0 {
80
+ return errors.New("no metrics collected")
81
+ }
82
+
83
+ return nil
84
+}
85
+
86
+func (ig *IntelGPU) Charts() *module.Charts {
87
+ return ig.charts
88
+}
89
+
90
+func (ig *IntelGPU) Collect() map[string]int64 {
91
+ mx, err := ig.collect()
92
+ if err != nil {
93
+ ig.Error(err)
94
+ }
95
+
96
+ if len(mx) == 0 {
97
+ return nil
98
+ }
99
+
100
+ return mx
101
+}
102
+
103
+func (ig *IntelGPU) Cleanup() {
104
+ if ig.exec != nil {
105
+ if err := ig.exec.stop(); err != nil {
106
+ ig.Error(err)
107
+ }
108
+ ig.exec = nil
109
+ }
110
+}
src/go/collectors/go.d.plugin/modules/intelgpu/intelgpu_test.go
new
+200
@@ -0,0 +1,200 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package intelgpu
4
+
5
+import (
6
+ "errors"
7
+ "os"
8
+ "testing"
9
+
10
+ "github.com/stretchr/testify/assert"
11
+ "github.com/stretchr/testify/require"
12
+)
13
+
14
+var (
15
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
16
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
17
+
18
+ dataIntelTopGpuJSON, _ = os.ReadFile("testdata/igt.json")
19
+)
20
+
21
+func Test_testDataIsValid(t *testing.T) {
22
+ for name, data := range map[string][]byte{
23
+ "dataConfigJSON": dataConfigJSON,
24
+ "dataConfigYAML": dataConfigYAML,
25
+ "dataIntelTopGpuJSON": dataIntelTopGpuJSON,
26
+ } {
27
+ require.NotNil(t, data, name)
28
+ }
29
+}
30
+
31
+func TestIntelGPU_Init(t *testing.T) {
32
+ tests := map[string]struct {
33
+ prepare func(igt *IntelGPU)
34
+ wantFail bool
35
+ }{
36
+ "fails if can't find intel_gpu_top": {
37
+ wantFail: true,
38
+ prepare: func(igt *IntelGPU) {
39
+ igt.binName += "!!!"
40
+ },
41
+ },
42
+ }
43
+
44
+ for name, test := range tests {
45
+ t.Run(name, func(t *testing.T) {
46
+ igt := New()
47
+
48
+ test.prepare(igt)
49
+
50
+ if test.wantFail {
51
+ assert.Error(t, igt.Init())
52
+ } else {
53
+ assert.NoError(t, igt.Init())
54
+ }
55
+ })
56
+ }
57
+}
58
+
59
+func TestIntelGPU_Check(t *testing.T) {
60
+ tests := map[string]struct {
61
+ prepareMock func() *mockIntelGpuTop
62
+ wantFail bool
63
+ }{
64
+ "success case": {
65
+ prepareMock: prepareMockOK,
66
+ wantFail: false,
67
+ },
68
+ "fail on error": {
69
+ prepareMock: prepareMockErrOnGPUSummaryJson,
70
+ wantFail: true,
71
+ },
72
+ }
73
+
74
+ for name, test := range tests {
75
+ t.Run(name, func(t *testing.T) {
76
+ igt := New()
77
+ mock := test.prepareMock()
78
+ igt.exec = mock
79
+
80
+ if test.wantFail {
81
+ assert.Error(t, igt.Check())
82
+ } else {
83
+ assert.NoError(t, igt.Check())
84
+ }
85
+ })
86
+ }
87
+}
88
+
89
+func TestIntelGPU_Collect(t *testing.T) {
90
+ tests := map[string]struct {
91
+ prepareMock func() *mockIntelGpuTop
92
+ wantMetrics map[string]int64
93
+ }{
94
+ "success case": {
95
+ prepareMock: prepareMockOK,
96
+ wantMetrics: map[string]int64{
97
+ "engine_Blitter/0_busy": 0,
98
+ "engine_Render/3D/0_busy": 9609,
99
+ "engine_Video/0_busy": 7295,
100
+ "engine_Video/1_busy": 7740,
101
+ "engine_VideoEnhance/0_busy": 0,
102
+ "frequency_actual": 125308,
103
+ "power_gpu": 323,
104
+ "power_package": 1665,
105
+ },
106
+ },
107
+ "fail on error": {
108
+ prepareMock: prepareMockErrOnGPUSummaryJson,
109
+ wantMetrics: nil,
110
+ },
111
+ }
112
+
113
+ for name, test := range tests {
114
+ t.Run(name, func(t *testing.T) {
115
+ igt := New()
116
+ mock := test.prepareMock()
117
+ igt.exec = mock
118
+
119
+ mx := igt.Collect()
120
+
121
+ assert.Equal(t, test.wantMetrics, mx)
122
+ if len(test.wantMetrics) > 0 {
123
+ assert.Len(t, *igt.Charts(), len(charts)+len(igt.engines))
124
+ }
125
+ })
126
+ }
127
+}
128
+
129
+func TestIntelGPU_Cleanup(t *testing.T) {
130
+ tests := map[string]struct {
131
+ prepare func() *IntelGPU
132
+ }{
133
+ "not initialized exec": {
134
+ prepare: func() *IntelGPU {
135
+ return New()
136
+ },
137
+ },
138
+ "after check": {
139
+ prepare: func() *IntelGPU {
140
+ igt := New()
141
+ igt.exec = prepareMockOK()
142
+ _ = igt.Check()
143
+ return igt
144
+ },
145
+ },
146
+ "after collect": {
147
+ prepare: func() *IntelGPU {
148
+ igt := New()
149
+ igt.exec = prepareMockOK()
150
+ _ = igt.Collect()
151
+ return igt
152
+ },
153
+ },
154
+ }
155
+
156
+ for name, test := range tests {
157
+ t.Run(name, func(t *testing.T) {
158
+ igt := test.prepare()
159
+
160
+ mock, ok := igt.exec.(*mockIntelGpuTop)
161
+
162
+ assert.NotPanics(t, igt.Cleanup)
163
+
164
+ if ok {
165
+ assert.True(t, mock.stopCalled)
166
+ }
167
+ })
168
+ }
169
+}
170
+
171
+func prepareMockOK() *mockIntelGpuTop {
172
+ return &mockIntelGpuTop{
173
+ gpuSummaryJson: dataIntelTopGpuJSON,
174
+ }
175
+}
176
+
177
+func prepareMockErrOnGPUSummaryJson() *mockIntelGpuTop {
178
+ return &mockIntelGpuTop{
179
+ errOnQueryGPUSummaryJson: true,
180
+ }
181
+}
182
+
183
+type mockIntelGpuTop struct {
184
+ errOnQueryGPUSummaryJson bool
185
+ gpuSummaryJson []byte
186
+
187
+ stopCalled bool
188
+}
189
+
190
+func (m *mockIntelGpuTop) queryGPUSummaryJson() ([]byte, error) {
191
+ if m.errOnQueryGPUSummaryJson {
192
+ return nil, errors.New("error on mock.queryGPUSummaryJson()")
193
+ }
194
+ return m.gpuSummaryJson, nil
195
+}
196
+
197
+func (m *mockIntelGpuTop) stop() error {
198
+ m.stopCalled = true
199
+ return nil
200
+}
src/go/collectors/go.d.plugin/modules/intelgpu/metadata.yaml
new
+115
@@ -0,0 +1,115 @@
1
+plugin_name: go.d.plugin
2
+modules:
3
+ - meta:
4
+ id: collector-go.d.plugin-intelgpu
5
+ plugin_name: go.d.plugin
6
+ module_name: intelgpu
7
+ monitored_instance:
8
+ name: Intel GPU
9
+ link: https://www.intel.com/
10
+ icon_filename: intel.svg
11
+ categories:
12
+ - data-collection.hardware-devices-and-sensors
13
+ keywords:
14
+ - intel
15
+ - gpu
16
+ - hardware
17
+ related_resources:
18
+ integrations:
19
+ list: []
20
+ info_provided_to_referring_integrations:
21
+ description: ""
22
+ most_popular: false
23
+ overview:
24
+ data_collection:
25
+ metrics_description: |
26
+ This collector monitors Intel integrated GPUs performance metrics using
27
+ the [intel_gpu_top](https://manpages.debian.org/testing/intel-gpu-tools/intel_gpu_top.1.en.html) CLI tool.
28
+ method_description: ""
29
+ supported_platforms:
30
+ include: []
31
+ exclude: []
32
+ multi_instance: true
33
+ additional_permissions:
34
+ description: ""
35
+ default_behavior:
36
+ auto_detection:
37
+ description: ""
38
+ limits:
39
+ description: ""
40
+ performance_impact:
41
+ description: ""
42
+ setup:
43
+ prerequisites:
44
+ list: []
45
+ configuration:
46
+ file:
47
+ name: go.d/intelgpu.conf
48
+ options:
49
+ description: |
50
+ The following options can be defined globally: update_every.
51
+ folding:
52
+ title: Config options
53
+ enabled: true
54
+ list:
55
+ - name: update_every
56
+ description: Data collection frequency.
57
+ default_value: 1
58
+ required: false
59
+ - name: binary_path
60
+ description: Path to the intel_gpu_top binary. If an absolute path is provided, the collector will use it directly; otherwise, it will search for the binary in directories specified in the PATH environment variable.
61
+ default_value: /usr/bin/intel_gpu_top
62
+ required: false
63
+ examples:
64
+ folding:
65
+ title: Config
66
+ enabled: true
67
+ list:
68
+ - name: Custom binary path
69
+ description: The executable is not in the directories specified in the PATH environment variable.
70
+ config: |
71
+ jobs:
72
+ - name: nvidia_smi
73
+ binary_path: /usr/local/sbin/intel_gpu_top
74
+ troubleshooting:
75
+ problems:
76
+ list: []
77
+ alerts: []
78
+ metrics:
79
+ folding:
80
+ title: Metrics
81
+ enabled: false
82
+ description: ""
83
+ availability: []
84
+ scopes:
85
+ - name: global
86
+ description: These metrics refer to the Intel GPU.
87
+ labels: []
88
+ metrics:
89
+ - name: intelgpu.frequency
90
+ description: Intel GPU frequency
91
+ unit: MHz
92
+ chart_type: line
93
+ dimensions:
94
+ - name: frequency
95
+ - name: intelgpu.power
96
+ description: Intel GPU power
97
+ unit: Watts
98
+ chart_type: line
99
+ dimensions:
100
+ - name: gpu
101
+ - name: package
102
+ - name: engine
103
+ description: These metrics refer to the GPU hardware engine.
104
+ labels:
105
+ - name: engine
106
+ description: Engine name (Render/3D, Blitter, VideoEnhance, Video, Compute).
107
+ - name: engine_instance
108
+ description: Engine instance (e.g. Render/3D/0, Video/0, Video/1).
109
+ metrics:
110
+ - name: intelgpu.engine_busy_perc
111
+ description: Intel GPU engine busy time percentage
112
+ unit: percentage
113
+ chart_type: line
114
+ dimensions:
115
+ - name: busy
src/go/collectors/go.d.plugin/modules/intelgpu/testdata/config.json
new
+4
@@ -0,0 +1,4 @@
1
+{
2
+ "update_every": 123,
3
+ "binary_path": "ok"
4
+}
src/go/collectors/go.d.plugin/modules/intelgpu/testdata/config.yaml
new
+2
@@ -0,0 +1,2 @@
1
+update_every: 123
2
+binary_path: "ok"
src/go/collectors/go.d.plugin/modules/intelgpu/testdata/igt.json
new
+80
@@ -0,0 +1,80 @@
1
+{
2
+ "period": {
3
+ "duration": 1055.794145,
4
+ "unit": "ms"
5
+ },
6
+ "frequency": {
7
+ "requested": 1449.146131,
8
+ "actual": 1253.085184,
9
+ "unit": "MHz"
10
+ },
11
+ "interrupts": {
12
+ "count": 1757.918443,
13
+ "unit": "irq/s"
14
+ },
15
+ "rc6": {
16
+ "value": 0.000000,
17
+ "unit": "%"
18
+ },
19
+ "power": {
20
+ "GPU": 3.233528,
21
+ "Package": 16.658620,
22
+ "unit": "W"
23
+ },
24
+ "engines": {
25
+ "Render/3D/0": {
26
+ "busy": 96.092944,
27
+ "sema": 0.000000,
28
+ "wait": 0.000000,
29
+ "unit": "%"
30
+ },
31
+ "Blitter/0": {
32
+ "busy": 0.000000,
33
+ "sema": 0.000000,
34
+ "wait": 0.000000,
35
+ "unit": "%"
36
+ },
37
+ "Video/0": {
38
+ "busy": 72.950675,
39
+ "sema": 0.000000,
40
+ "wait": 0.000000,
41
+ "unit": "%"
42
+ },
43
+ "Video/1": {
44
+ "busy": 77.402254,
45
+ "sema": 0.000000,
46
+ "wait": 0.000000,
47
+ "unit": "%"
48
+ },
49
+ "VideoEnhance/0": {
50
+ "busy": 0.000000,
51
+ "sema": 0.000000,
52
+ "wait": 0.000000,
53
+ "unit": "%"
54
+ }
55
+ },
56
+ "clients": {
57
+ "4292239459": {
58
+ "name": "ffmpeg",
59
+ "pid": "2727837",
60
+ "engine-classes": {
61
+ "Render/3D": {
62
+ "busy": "101.396726",
63
+ "unit": "%"
64
+ },
65
+ "Blitter": {
66
+ "busy": "0.000000",
67
+ "unit": "%"
68
+ },
69
+ "Video": {
70
+ "busy": "159.292435",
71
+ "unit": "%"
72
+ },
73
+ "VideoEnhance": {
74
+ "busy": "0.000000",
75
+ "unit": "%"
76
+ }
77
+ }
78
+ }
79
+ }
80
+}