pinokiod 7.3.5 → 7.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/resource_usage/gpu.js +1078 -270
- package/kernel/resource_usage/index.js +9 -4
- package/package.json +2 -1
- package/server/index.js +3 -0
- package/server/views/install.ejs +4 -7
- package/test/resource-usage-gpu.test.js +297 -70
|
@@ -55,8 +55,7 @@ class ResourceUsageService {
|
|
|
55
55
|
})
|
|
56
56
|
this.gpuSampler = new GpuSampler({
|
|
57
57
|
kernel: this.kernel,
|
|
58
|
-
ttlMs:
|
|
59
|
-
timeoutMs: 2500
|
|
58
|
+
ttlMs: 5000
|
|
60
59
|
})
|
|
61
60
|
this.cpuAverages = new Map()
|
|
62
61
|
this.workspaceCache = new Map()
|
|
@@ -76,6 +75,12 @@ class ResourceUsageService {
|
|
|
76
75
|
return preferences
|
|
77
76
|
}
|
|
78
77
|
|
|
78
|
+
stop() {
|
|
79
|
+
if (this.gpuSampler && typeof this.gpuSampler.stop === "function") {
|
|
80
|
+
this.gpuSampler.stop()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
79
84
|
getShellRootGroups() {
|
|
80
85
|
if (!this.kernel || !this.kernel.shell || typeof this.kernel.path !== "function") {
|
|
81
86
|
return new Map()
|
|
@@ -243,8 +248,8 @@ class ResourceUsageService {
|
|
|
243
248
|
? await this.macFootprintSampler.getFootprintByPid(this.selectFootprintPids(allPids))
|
|
244
249
|
: null
|
|
245
250
|
|
|
246
|
-
const gpuSnapshot = preferences.show_vram && allPids.size > 0
|
|
247
|
-
? await this.gpuSampler.getSnapshot()
|
|
251
|
+
const gpuSnapshot = preferences.show_vram && this.platform !== "darwin" && allPids.size > 0
|
|
252
|
+
? await this.gpuSampler.getSnapshot(allPids)
|
|
248
253
|
: null
|
|
249
254
|
|
|
250
255
|
const nextCache = new Map()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinokiod",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"key-store": "^1.2.0",
|
|
57
57
|
"kill-sync": "^1.0.3",
|
|
58
58
|
"kleur": "^4.1.5",
|
|
59
|
+
"koffi": "^3.0.2",
|
|
59
60
|
"lodash": "^4.17.21",
|
|
60
61
|
"marked": "^5.0.1",
|
|
61
62
|
"mime-types": "^2.1.35",
|
package/server/index.js
CHANGED
package/server/views/install.ejs
CHANGED
|
@@ -19,10 +19,11 @@ document.documentElement.dataset.installSpinnerVariant = "grid-shift"
|
|
|
19
19
|
<style>
|
|
20
20
|
body {
|
|
21
21
|
--install-status-bg: var(--pinokio-chrome-accent-bg-light);
|
|
22
|
+
--install-spinner-fg: rgba(0,0,0,0.8);
|
|
22
23
|
--install-status-fg: var(--pinokio-chrome-accent-fg-light);
|
|
23
24
|
--install-status-fg-soft: rgba(225, 178, 97, 0.34);
|
|
24
25
|
--install-status-border: rgba(15, 23, 42, 0.12);
|
|
25
|
-
--install-status-detail: rgba(
|
|
26
|
+
--install-status-detail: rgba(0, 0, 0, 0.6);
|
|
26
27
|
height: 100%;
|
|
27
28
|
overflow: hidden;
|
|
28
29
|
/*
|
|
@@ -33,6 +34,7 @@ body {
|
|
|
33
34
|
}
|
|
34
35
|
body.dark {
|
|
35
36
|
--install-status-bg: var(--pinokio-chrome-accent-bg-dark);
|
|
37
|
+
--install-spinner-fg: white;
|
|
36
38
|
--install-status-fg: var(--pinokio-chrome-accent-fg-dark);
|
|
37
39
|
--install-status-fg-soft: rgba(194, 178, 138, 0.28);
|
|
38
40
|
--install-status-border: rgba(255, 255, 255, 0.08);
|
|
@@ -114,9 +116,6 @@ body.dark {
|
|
|
114
116
|
width: 100%;
|
|
115
117
|
margin: 0;
|
|
116
118
|
overflow: hidden;
|
|
117
|
-
border-top: 1px solid var(--install-status-border);
|
|
118
|
-
background: var(--install-status-bg);
|
|
119
|
-
color: var(--install-status-fg);
|
|
120
119
|
}
|
|
121
120
|
#status-screen code {
|
|
122
121
|
font-family: "RobotoMono", monospace;
|
|
@@ -132,7 +131,6 @@ body.dark {
|
|
|
132
131
|
flex: 1;
|
|
133
132
|
}
|
|
134
133
|
.install-status-title {
|
|
135
|
-
color: var(--install-status-fg);
|
|
136
134
|
font-size: 15px;
|
|
137
135
|
line-height: 1.2;
|
|
138
136
|
font-weight: 700;
|
|
@@ -189,13 +187,12 @@ body.dark {
|
|
|
189
187
|
border-radius: 2px;
|
|
190
188
|
}
|
|
191
189
|
.install-status-grid-anchor {
|
|
192
|
-
background: var(--install-status-fg);
|
|
193
190
|
opacity: 0.16;
|
|
194
191
|
}
|
|
195
192
|
.install-status-grid-tile,
|
|
196
193
|
.install-status-grid-chaser,
|
|
197
194
|
.install-status-grid-pixel {
|
|
198
|
-
background: var(--install-
|
|
195
|
+
background: var(--install-spinner-fg);
|
|
199
196
|
}
|
|
200
197
|
.install-status-grid-pixel {
|
|
201
198
|
width: 4px;
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
const assert = require("node:assert/strict")
|
|
2
|
+
const fs = require("node:fs")
|
|
3
|
+
const os = require("node:os")
|
|
4
|
+
const path = require("node:path")
|
|
2
5
|
const test = require("node:test")
|
|
3
6
|
|
|
4
7
|
const {
|
|
5
8
|
GpuSampler,
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
NvmlGpuMemoryClient,
|
|
10
|
+
collectLinuxDrmFdinfoProcesses,
|
|
11
|
+
decodeWindowsMultiSz,
|
|
12
|
+
extractPidFromWindowsGpuInstance,
|
|
13
|
+
isDedicatedDrmMemoryRegion,
|
|
14
|
+
parseLinuxDrmFdinfo
|
|
8
15
|
} = require("../kernel/resource_usage/gpu")
|
|
9
16
|
|
|
10
17
|
const MIB = 1024 * 1024
|
|
@@ -16,111 +23,331 @@ function gpuProcess(pid, bytes) {
|
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
test("
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
test("extractPidFromWindowsGpuInstance handles full PDH counter paths", () => {
|
|
27
|
+
assert.equal(extractPidFromWindowsGpuInstance("app_pid_3456_phys_0"), 3456)
|
|
28
|
+
assert.equal(extractPidFromWindowsGpuInstance("\\\\HOST\\GPU Process Memory(pid_1234_luid_0x00000000_phys_0)\\Dedicated Usage"), 1234)
|
|
29
|
+
assert.equal(extractPidFromWindowsGpuInstance("\\\\HOST\\GPU Process Memory(_total)\\Dedicated Usage"), null)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("decodeWindowsMultiSz decodes double-null UTF-16 string lists", () => {
|
|
33
|
+
const text = "one\u0000two\u0000\u0000"
|
|
34
|
+
const buffer = Buffer.from(text, "utf16le")
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(decodeWindowsMultiSz(buffer, text.length), ["one", "two"])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("parseLinuxDrmFdinfo counts dedicated DRM memory regions only", () => {
|
|
40
|
+
const amdgpu = parseLinuxDrmFdinfo([
|
|
41
|
+
"drm-driver:\tamdgpu",
|
|
42
|
+
"drm-pdev:\t0000:03:00.0",
|
|
43
|
+
"drm-client-id:\t17",
|
|
44
|
+
"drm-memory-vram:\t4 MiB",
|
|
45
|
+
"drm-memory-gtt:\t128 MiB"
|
|
24
46
|
].join("\n"))
|
|
25
47
|
|
|
26
|
-
|
|
27
|
-
|
|
48
|
+
const intel = parseLinuxDrmFdinfo([
|
|
49
|
+
"drm-driver:\ti915",
|
|
50
|
+
"drm-pdev:\t0000:00:02.0",
|
|
51
|
+
"drm-client-id:\t9",
|
|
52
|
+
"drm-resident-local0:\t8 MiB",
|
|
53
|
+
"drm-resident-system:\t64 MiB"
|
|
54
|
+
].join("\n"))
|
|
55
|
+
|
|
56
|
+
assert.equal(amdgpu.dedicatedBytes, 4 * MIB)
|
|
57
|
+
assert.equal(intel.dedicatedBytes, 8 * MIB)
|
|
58
|
+
assert.equal(isDedicatedDrmMemoryRegion("vram"), true)
|
|
59
|
+
assert.equal(isDedicatedDrmMemoryRegion("local0"), true)
|
|
60
|
+
assert.equal(isDedicatedDrmMemoryRegion("gtt"), false)
|
|
61
|
+
assert.equal(isDedicatedDrmMemoryRegion("system"), false)
|
|
28
62
|
})
|
|
29
63
|
|
|
30
|
-
test("
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
64
|
+
test("collectLinuxDrmFdinfoProcesses deduplicates DRM client fds", async () => {
|
|
65
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pinokio-drm-fdinfo-"))
|
|
66
|
+
try {
|
|
67
|
+
await fs.promises.mkdir(path.join(root, "1234", "fdinfo"), { recursive: true })
|
|
68
|
+
await fs.promises.mkdir(path.join(root, "5678", "fdinfo"), { recursive: true })
|
|
69
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "3"), [
|
|
70
|
+
"drm-driver:\tamdgpu",
|
|
71
|
+
"drm-pdev:\t0000:03:00.0",
|
|
72
|
+
"drm-client-id:\t17",
|
|
73
|
+
"drm-resident-vram:\t12 MiB"
|
|
74
|
+
].join("\n"))
|
|
75
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "4"), [
|
|
76
|
+
"drm-driver:\tamdgpu",
|
|
77
|
+
"drm-pdev:\t0000:03:00.0",
|
|
78
|
+
"drm-client-id:\t17",
|
|
79
|
+
"drm-resident-vram:\t11 MiB"
|
|
80
|
+
].join("\n"))
|
|
81
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "5"), [
|
|
82
|
+
"drm-driver:\tamdgpu",
|
|
83
|
+
"drm-pdev:\t0000:03:00.0",
|
|
84
|
+
"drm-client-id:\t18",
|
|
85
|
+
"drm-resident-vram:\t5 MiB"
|
|
86
|
+
].join("\n"))
|
|
87
|
+
await fs.promises.writeFile(path.join(root, "5678", "fdinfo", "9"), [
|
|
88
|
+
"drm-driver:\tamdgpu",
|
|
89
|
+
"drm-pdev:\t0000:03:00.0",
|
|
90
|
+
"drm-client-id:\t22",
|
|
91
|
+
"drm-resident-gtt:\t64 MiB"
|
|
92
|
+
].join("\n"))
|
|
36
93
|
|
|
37
|
-
|
|
38
|
-
|
|
94
|
+
const processes = await collectLinuxDrmFdinfoProcesses([1234, 5678], { procRoot: root })
|
|
95
|
+
|
|
96
|
+
assert.equal(processes.get(1234).usedGpuMemoryBytes, 17 * MIB)
|
|
97
|
+
assert.equal(processes.has(5678), false)
|
|
98
|
+
} finally {
|
|
99
|
+
await fs.promises.rm(root, { recursive: true, force: true })
|
|
100
|
+
}
|
|
39
101
|
})
|
|
40
102
|
|
|
41
|
-
test("GpuSampler uses Windows
|
|
42
|
-
const sampler = new GpuSampler({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
provider: "windows-gpu-process-memory",
|
|
47
|
-
processes: new Map([
|
|
103
|
+
test("GpuSampler uses Windows PDH only on Windows", async () => {
|
|
104
|
+
const sampler = new GpuSampler({
|
|
105
|
+
platform: "win32",
|
|
106
|
+
windowsPdhClient: {
|
|
107
|
+
collect: () => new Map([
|
|
48
108
|
[1234, gpuProcess(1234, 500 * MIB)]
|
|
49
|
-
])
|
|
50
|
-
error: null
|
|
109
|
+
])
|
|
51
110
|
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
114
|
+
|
|
115
|
+
assert.equal(snapshot.available, true)
|
|
116
|
+
assert.deepEqual(snapshot.providers, ["windows-pdh"])
|
|
117
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 500 * MIB)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test("GpuSampler reports Windows VRAM unavailable when PDH fails", async () => {
|
|
121
|
+
const originalWarn = console.warn
|
|
122
|
+
console.warn = () => {}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const sampler = new GpuSampler({
|
|
126
|
+
platform: "win32",
|
|
127
|
+
windowsPdhClient: {
|
|
128
|
+
collect: () => {
|
|
129
|
+
throw new Error("pdh unavailable")
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
135
|
+
|
|
136
|
+
assert.equal(snapshot.available, false)
|
|
137
|
+
assert.deepEqual(snapshot.providers, ["windows-pdh"])
|
|
138
|
+
assert.equal(snapshot.processes.size, 0)
|
|
139
|
+
assert.equal(snapshot.errors[0].provider, "windows-pdh")
|
|
140
|
+
} finally {
|
|
141
|
+
console.warn = originalWarn
|
|
52
142
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
error: null
|
|
61
|
-
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test("GpuSampler logs provider failures once per backoff window", async () => {
|
|
146
|
+
const originalWarn = console.warn
|
|
147
|
+
const warnings = []
|
|
148
|
+
console.warn = (...args) => {
|
|
149
|
+
warnings.push(args)
|
|
62
150
|
}
|
|
63
|
-
sampler.collectAmd = async () => null
|
|
64
151
|
|
|
65
|
-
|
|
152
|
+
try {
|
|
153
|
+
const sampler = new GpuSampler({
|
|
154
|
+
platform: "win32",
|
|
155
|
+
windowsPdhClient: {
|
|
156
|
+
collect: () => {
|
|
157
|
+
const error = new Error("pdh unavailable")
|
|
158
|
+
error.code = "PDH_TEST"
|
|
159
|
+
throw error
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
})
|
|
66
163
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
164
|
+
await sampler.collect(new Set([1234]))
|
|
165
|
+
await sampler.collect(new Set([1234]))
|
|
166
|
+
|
|
167
|
+
assert.equal(warnings.length, 1)
|
|
168
|
+
assert.equal(warnings[0][0], "[resource-usage:gpu] provider failed")
|
|
169
|
+
assert.deepEqual(warnings[0][1], {
|
|
170
|
+
provider: "windows-pdh",
|
|
171
|
+
platform: "win32",
|
|
172
|
+
pid_count: 1,
|
|
173
|
+
error: "pdh unavailable",
|
|
174
|
+
code: "PDH_TEST"
|
|
175
|
+
})
|
|
176
|
+
} finally {
|
|
177
|
+
console.warn = originalWarn
|
|
178
|
+
}
|
|
70
179
|
})
|
|
71
180
|
|
|
72
|
-
test("GpuSampler
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
181
|
+
test("GpuSampler uses Linux DRM fdinfo before native library providers", async () => {
|
|
182
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pinokio-drm-sampler-"))
|
|
183
|
+
try {
|
|
184
|
+
await fs.promises.mkdir(path.join(root, "1234", "fdinfo"), { recursive: true })
|
|
185
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "3"), [
|
|
186
|
+
"drm-driver:\ti915",
|
|
187
|
+
"drm-pdev:\t0000:00:02.0",
|
|
188
|
+
"drm-client-id:\t4",
|
|
189
|
+
"drm-resident-local0:\t32 MiB"
|
|
190
|
+
].join("\n"))
|
|
191
|
+
|
|
192
|
+
const sampler = new GpuSampler({ platform: "linux", procRoot: root })
|
|
193
|
+
let nvmlCalls = 0
|
|
194
|
+
let amdCalls = 0
|
|
195
|
+
let rocmCalls = 0
|
|
196
|
+
sampler.collectNvml = async () => {
|
|
197
|
+
nvmlCalls += 1
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
sampler.collectAmdSmi = async () => {
|
|
201
|
+
amdCalls += 1
|
|
202
|
+
return null
|
|
203
|
+
}
|
|
204
|
+
sampler.collectRocmSmi = async () => {
|
|
205
|
+
rocmCalls += 1
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
210
|
+
|
|
211
|
+
assert.equal(nvmlCalls, 0)
|
|
212
|
+
assert.equal(amdCalls, 0)
|
|
213
|
+
assert.equal(rocmCalls, 0)
|
|
214
|
+
assert.deepEqual(snapshot.providers, ["linux-drm-fdinfo"])
|
|
215
|
+
assert.equal(snapshot.available, true)
|
|
216
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 32 * MIB)
|
|
217
|
+
} finally {
|
|
218
|
+
await fs.promises.rm(root, { recursive: true, force: true })
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test("GpuSampler uses NVML when fdinfo does not cover target PID", async () => {
|
|
223
|
+
const sampler = new GpuSampler({
|
|
224
|
+
platform: "linux",
|
|
225
|
+
nvmlClient: {
|
|
226
|
+
collect: () => new Map([
|
|
227
|
+
[1234, gpuProcess(1234, 700 * MIB)]
|
|
228
|
+
])
|
|
229
|
+
}
|
|
79
230
|
})
|
|
80
|
-
sampler.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
231
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
232
|
+
let amdCalls = 0
|
|
233
|
+
sampler.collectAmdSmi = async () => {
|
|
234
|
+
amdCalls += 1
|
|
235
|
+
return null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
239
|
+
|
|
240
|
+
assert.equal(amdCalls, 0)
|
|
241
|
+
assert.deepEqual(snapshot.providers, ["linux-nvml"])
|
|
242
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 700 * MIB)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test("NvmlGpuMemoryClient sums the same PID across devices", () => {
|
|
246
|
+
const client = new NvmlGpuMemoryClient({ koffi: { sizeof: () => 1 } })
|
|
247
|
+
const compute = { name: "compute" }
|
|
248
|
+
const graphics = { name: "graphics" }
|
|
249
|
+
client.init = () => {}
|
|
250
|
+
client.getDeviceHandles = () => ["gpu0", "gpu1"]
|
|
251
|
+
client.functions = { compute, graphics, mps: null }
|
|
252
|
+
client.collectProcessList = (device, entry) => {
|
|
253
|
+
const samples = {
|
|
254
|
+
"gpu0:compute": [{ pid: 1234, usedGpuMemory: 300 * MIB }],
|
|
255
|
+
"gpu0:graphics": [{ pid: 1234, usedGpuMemory: 250 * MIB }],
|
|
256
|
+
"gpu1:compute": [{ pid: 1234, usedGpuMemory: 400 * MIB }],
|
|
257
|
+
"gpu1:graphics": [{ pid: 1234, usedGpuMemory: 100 * MIB }]
|
|
88
258
|
}
|
|
259
|
+
return samples[`${device}:${entry && entry.name}`] || []
|
|
89
260
|
}
|
|
90
|
-
sampler.collectAmd = async () => null
|
|
91
261
|
|
|
92
|
-
const
|
|
262
|
+
const processes = client.collect([1234])
|
|
93
263
|
|
|
94
|
-
assert.equal(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
264
|
+
assert.equal(processes.get(1234).usedGpuMemoryBytes, 700 * MIB)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
test("GpuSampler uses AMD SMI after fdinfo and NVML miss", async () => {
|
|
269
|
+
const sampler = new GpuSampler({
|
|
270
|
+
platform: "linux",
|
|
271
|
+
amdSmiClient: {
|
|
272
|
+
collect: () => new Map([
|
|
273
|
+
[2222, gpuProcess(2222, 300 * MIB)]
|
|
274
|
+
])
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
278
|
+
sampler.collectNvml = async () => null
|
|
279
|
+
let rocmCalls = 0
|
|
280
|
+
sampler.collectRocmSmi = async () => {
|
|
281
|
+
rocmCalls += 1
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const snapshot = await sampler.collect(new Set([2222]))
|
|
286
|
+
|
|
287
|
+
assert.equal(rocmCalls, 0)
|
|
288
|
+
assert.deepEqual(snapshot.providers, ["linux-amdsmi"])
|
|
289
|
+
assert.equal(snapshot.processes.get(2222).usedGpuMemoryBytes, 300 * MIB)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test("GpuSampler uses ROCm SMI after AMD SMI misses", async () => {
|
|
293
|
+
const sampler = new GpuSampler({
|
|
294
|
+
platform: "linux",
|
|
295
|
+
rocmSmiClient: {
|
|
296
|
+
collect: () => new Map([
|
|
297
|
+
[3333, gpuProcess(3333, 200 * MIB)]
|
|
298
|
+
])
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
302
|
+
sampler.collectNvml = async () => null
|
|
303
|
+
sampler.collectAmdSmi = async () => null
|
|
304
|
+
|
|
305
|
+
const snapshot = await sampler.collect(new Set([3333]))
|
|
306
|
+
|
|
307
|
+
assert.deepEqual(snapshot.providers, ["linux-rocm-smi"])
|
|
308
|
+
assert.equal(snapshot.processes.get(3333).usedGpuMemoryBytes, 200 * MIB)
|
|
98
309
|
})
|
|
99
310
|
|
|
100
311
|
test("GpuSampler merges overlapping provider samples by PID without double-counting", async () => {
|
|
101
312
|
const sampler = new GpuSampler({ platform: "linux" })
|
|
102
|
-
sampler.
|
|
103
|
-
provider: "
|
|
313
|
+
sampler.collectLinuxDrmFdinfo = async () => ({
|
|
314
|
+
provider: "linux-drm-fdinfo",
|
|
104
315
|
processes: new Map([
|
|
105
|
-
[1234, gpuProcess(1234, 300 * MIB)]
|
|
106
|
-
[3333, gpuProcess(3333, 200 * MIB)]
|
|
316
|
+
[1234, gpuProcess(1234, 300 * MIB)]
|
|
107
317
|
]),
|
|
108
318
|
error: null
|
|
109
319
|
})
|
|
110
|
-
sampler.
|
|
111
|
-
provider: "
|
|
320
|
+
sampler.collectNvml = async () => ({
|
|
321
|
+
provider: "linux-nvml",
|
|
112
322
|
processes: new Map([
|
|
113
323
|
[1234, gpuProcess(1234, 500 * MIB)],
|
|
114
|
-
[
|
|
324
|
+
[3333, gpuProcess(3333, 200 * MIB)]
|
|
115
325
|
]),
|
|
116
326
|
error: null
|
|
117
327
|
})
|
|
328
|
+
sampler.collectAmdSmi = async () => null
|
|
329
|
+
sampler.collectRocmSmi = async () => null
|
|
118
330
|
|
|
119
331
|
const snapshot = await sampler.collect()
|
|
120
332
|
|
|
121
333
|
assert.equal(snapshot.available, true)
|
|
122
|
-
assert.deepEqual(snapshot.providers, ["
|
|
334
|
+
assert.deepEqual(snapshot.providers, ["linux-drm-fdinfo", "linux-nvml"])
|
|
123
335
|
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 500 * MIB)
|
|
124
|
-
assert.equal(snapshot.processes.get(2222).usedGpuMemoryBytes, 100 * MIB)
|
|
125
336
|
assert.equal(snapshot.processes.get(3333).usedGpuMemoryBytes, 200 * MIB)
|
|
126
337
|
})
|
|
338
|
+
|
|
339
|
+
test("GpuSampler does not collect VRAM providers on macOS", async () => {
|
|
340
|
+
const sampler = new GpuSampler({ platform: "darwin" })
|
|
341
|
+
sampler.collectNvml = async () => {
|
|
342
|
+
throw new Error("NVML should not be queried on macOS")
|
|
343
|
+
}
|
|
344
|
+
sampler.collectAmdSmi = async () => {
|
|
345
|
+
throw new Error("AMD SMI should not be queried on macOS")
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const snapshot = await sampler.collect()
|
|
349
|
+
|
|
350
|
+
assert.equal(snapshot.available, false)
|
|
351
|
+
assert.deepEqual(snapshot.providers, [])
|
|
352
|
+
assert.equal(snapshot.processes.size, 0)
|
|
353
|
+
})
|