@wenathlan/saddle 1.8.18 → 2.0.2
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/Dockerfile +1339 -0
- package/README.md +33 -12
- package/alternatives.ts +1218 -0
- package/biome.json +153 -0
- package/boards.json +1517 -0
- package/compute.ts +3144 -0
- package/cores.json +1801 -0
- package/dist/acquisition.d.ts +359 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +629 -0
- package/dist/acquisition.js.map +1 -0
- package/dist/alternatives.d.ts +509 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +951 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/automation.d.ts +426 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +561 -0
- package/dist/automation.js.map +1 -0
- package/dist/browser.d.ts +562 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +1057 -0
- package/dist/browser.js.map +1 -0
- package/dist/capacitor.config.d.ts +13 -0
- package/dist/capacitor.config.d.ts.map +1 -0
- package/dist/capacitor.config.js +26 -0
- package/dist/capacitor.config.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/communication.d.ts +192 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +418 -0
- package/dist/communication.js.map +1 -0
- package/dist/compute.d.ts +1161 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +2273 -0
- package/dist/compute.js.map +1 -0
- package/dist/distribution.d.ts +529 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +811 -0
- package/dist/distribution.js.map +1 -0
- package/dist/execution.d.ts +530 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +761 -0
- package/dist/execution.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/foundation.d.ts +176 -0
- package/dist/foundation.d.ts.map +1 -0
- package/dist/foundation.js +309 -0
- package/dist/foundation.js.map +1 -0
- package/dist/index.d.ts +733 -123
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1018 -123
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +236 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +236 -0
- package/dist/integration.js.map +1 -0
- package/dist/intelligence.d.ts +94 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +145 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/{isolation/contracts.d.ts → isolation.d.ts} +12 -1
- package/dist/isolation.d.ts.map +1 -0
- package/dist/{isolation/contracts.js → isolation.js} +13 -1
- package/dist/isolation.js.map +1 -0
- package/dist/media.d.ts +344 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +986 -0
- package/dist/media.js.map +1 -0
- package/dist/modes.d.ts +359 -0
- package/dist/modes.d.ts.map +1 -0
- package/dist/modes.js +134 -0
- package/dist/modes.js.map +1 -0
- package/dist/{library/public.d.ts → operations.d.ts} +15 -1
- package/dist/operations.d.ts.map +1 -0
- package/dist/{library/public.js → operations.js} +18 -7
- package/dist/operations.js.map +1 -0
- package/dist/orchestrator.d.ts +1339 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +4308 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/performance.d.ts +416 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +827 -0
- package/dist/performance.js.map +1 -0
- package/dist/quantum.d.ts +810 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +2073 -0
- package/dist/quantum.js.map +1 -0
- package/dist/render.d.ts +381 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1073 -0
- package/dist/render.js.map +1 -0
- package/dist/scheduler.d.ts +596 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +1259 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/security.d.ts +501 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1170 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{server/node.js → server.js} +16 -2
- package/dist/server.js.map +1 -0
- package/dist/tiers.d.ts +1002 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +2165 -0
- package/dist/tiers.js.map +1 -0
- package/dist/virtual.d.ts +641 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1285 -0
- package/dist/virtual.js.map +1 -0
- package/dist/virtualcpu.d.ts +316 -0
- package/dist/virtualcpu.d.ts.map +1 -0
- package/dist/virtualcpu.js +1180 -0
- package/dist/virtualcpu.js.map +1 -0
- package/dist/virtualgpu.d.ts +345 -0
- package/dist/virtualgpu.d.ts.map +1 -0
- package/dist/virtualgpu.js +1000 -0
- package/dist/virtualgpu.js.map +1 -0
- package/dist/virtualization.d.ts +607 -0
- package/dist/virtualization.d.ts.map +1 -0
- package/dist/virtualization.js +1187 -0
- package/dist/virtualization.js.map +1 -0
- package/dist/virtualmemory.d.ts +818 -0
- package/dist/virtualmemory.d.ts.map +1 -0
- package/dist/virtualmemory.js +1796 -0
- package/dist/virtualmemory.js.map +1 -0
- package/dist/webscrape.d.ts +720 -0
- package/dist/webscrape.d.ts.map +1 -0
- package/dist/webscrape.js +2697 -0
- package/dist/webscrape.js.map +1 -0
- package/docker.config +769 -0
- package/docs/CONVERSA.txt +1523 -0
- package/docs/alternatives.md +499 -0
- package/docs/architecture-1.8.19.md +76 -0
- package/docs/architecture-1.8.19.mmd +33 -0
- package/docs/architecture-1.8.19.png +0 -0
- package/docs/architecture.md +355 -0
- package/docs/brancharchive-2026-08-18.md +18 -0
- package/docs/browser.md +7 -0
- package/docs/consolidation.md +70 -0
- package/docs/e2ugh-engine.md +528 -0
- package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
- package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
- package/docs/hardware.md +342 -0
- package/docs/optimization.md +126 -0
- package/docs/performance.md +499 -0
- package/docs/planning.1.8.18.md +94 -0
- package/docs/planning.1.8.19.md +49 -0
- package/docs/releasenotes-1.8.19.md +35 -0
- package/docs/research-1.8.19-virtual-browser.md +27 -0
- package/docs/security.md +454 -0
- package/docs/todo-1.8.18.md +12 -0
- package/docs/todo-1.8.19.md +241 -0
- package/docs/viability.md +777 -0
- package/docs/virtualization.md +335 -0
- package/docs/web-duplicatecleanup.md +9 -0
- package/docs/web-ideas.md +106 -0
- package/docs/workflowimprovements-2026-08-19.md +70 -0
- package/gpumonitor.cpp +1543 -0
- package/gpus.json +2408 -0
- package/index.ts +1478 -0
- package/media.ts +1337 -0
- package/mttg.config +1549 -0
- package/orchestrator.ts +5462 -0
- package/package.json +304 -56
- package/passage.config +1331 -0
- package/performance.ts +1049 -0
- package/processors.json +2217 -0
- package/qemu.config +1062 -0
- package/qemubridge.py +1340 -0
- package/quantum.ts +2393 -0
- package/render.ts +1325 -0
- package/scheduler.ts +1600 -0
- package/security.ts +1449 -0
- package/tiers.ts +2871 -0
- package/tsconfig.json +29 -0
- package/virtualcpu.ts +1273 -0
- package/virtualgpu.ts +1181 -0
- package/virtualhardware.c +1011 -0
- package/virtualhardware.json +739 -0
- package/virtualization.ts +1628 -0
- package/virtualizationcore.cpp +4635 -0
- package/virtualmemory.ts +2303 -0
- package/vm.config.json +1910 -0
- package/{extension → web/extension}/manifest.json +1 -1
- package/web/readme.md +382 -0
- package/web/tsconfig.json +25 -0
- package/dist/adapters/forge.d.ts +0 -17
- package/dist/adapters/forge.d.ts.map +0 -1
- package/dist/adapters/forge.js +0 -19
- package/dist/adapters/forge.js.map +0 -1
- package/dist/adapters/forgejo.d.ts +0 -49
- package/dist/adapters/forgejo.d.ts.map +0 -1
- package/dist/adapters/forgejo.js +0 -8
- package/dist/adapters/forgejo.js.map +0 -1
- package/dist/adapters/github.d.ts +0 -12
- package/dist/adapters/github.d.ts.map +0 -1
- package/dist/adapters/github.js +0 -20
- package/dist/adapters/github.js.map +0 -1
- package/dist/adapters/gitlab.d.ts +0 -17
- package/dist/adapters/gitlab.d.ts.map +0 -1
- package/dist/adapters/gitlab.js +0 -10
- package/dist/adapters/gitlab.js.map +0 -1
- package/dist/adapters/huggingface.d.ts +0 -17
- package/dist/adapters/huggingface.d.ts.map +0 -1
- package/dist/adapters/huggingface.js +0 -6
- package/dist/adapters/huggingface.js.map +0 -1
- package/dist/adapters/socket.d.ts +0 -11
- package/dist/adapters/socket.d.ts.map +0 -1
- package/dist/adapters/socket.js +0 -17
- package/dist/adapters/socket.js.map +0 -1
- package/dist/adapters/transport.d.ts +0 -7
- package/dist/adapters/transport.d.ts.map +0 -1
- package/dist/adapters/transport.js +0 -36
- package/dist/adapters/transport.js.map +0 -1
- package/dist/ai/chunk.d.ts +0 -2
- package/dist/ai/chunk.d.ts.map +0 -1
- package/dist/ai/chunk.js +0 -36
- package/dist/ai/chunk.js.map +0 -1
- package/dist/ai/llmstxt.d.ts +0 -6
- package/dist/ai/llmstxt.d.ts.map +0 -1
- package/dist/ai/llmstxt.js +0 -12
- package/dist/ai/llmstxt.js.map +0 -1
- package/dist/ai/provenance.d.ts +0 -22
- package/dist/ai/provenance.d.ts.map +0 -1
- package/dist/ai/provenance.js +0 -25
- package/dist/ai/provenance.js.map +0 -1
- package/dist/ai/rag.d.ts +0 -26
- package/dist/ai/rag.d.ts.map +0 -1
- package/dist/ai/rag.js +0 -22
- package/dist/ai/rag.js.map +0 -1
- package/dist/ai/tokens.d.ts +0 -15
- package/dist/ai/tokens.d.ts.map +0 -1
- package/dist/ai/tokens.js +0 -9
- package/dist/ai/tokens.js.map +0 -1
- package/dist/api/auth.d.ts +0 -16
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -19
- package/dist/api/auth.js.map +0 -1
- package/dist/api/contracts.d.ts +0 -28
- package/dist/api/contracts.d.ts.map +0 -1
- package/dist/api/contracts.js +0 -14
- package/dist/api/contracts.js.map +0 -1
- package/dist/api/control.d.ts +0 -10
- package/dist/api/control.d.ts.map +0 -1
- package/dist/api/control.js +0 -35
- package/dist/api/control.js.map +0 -1
- package/dist/api/http.d.ts +0 -7
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js +0 -12
- package/dist/api/http.js.map +0 -1
- package/dist/api/rate.d.ts +0 -28
- package/dist/api/rate.d.ts.map +0 -1
- package/dist/api/rate.js +0 -36
- package/dist/api/rate.js.map +0 -1
- package/dist/api/security.d.ts +0 -12
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -56
- package/dist/api/security.js.map +0 -1
- package/dist/api/service.d.ts +0 -5
- package/dist/api/service.d.ts.map +0 -1
- package/dist/api/service.js +0 -70
- package/dist/api/service.js.map +0 -1
- package/dist/apps/registry.d.ts +0 -33
- package/dist/apps/registry.d.ts.map +0 -1
- package/dist/apps/registry.js +0 -24
- package/dist/apps/registry.js.map +0 -1
- package/dist/binary/archive.d.ts +0 -27
- package/dist/binary/archive.d.ts.map +0 -1
- package/dist/binary/archive.js +0 -46
- package/dist/binary/archive.js.map +0 -1
- package/dist/binary/build.d.ts +0 -29
- package/dist/binary/build.d.ts.map +0 -1
- package/dist/binary/build.js +0 -17
- package/dist/binary/build.js.map +0 -1
- package/dist/binary/transform.d.ts +0 -70
- package/dist/binary/transform.d.ts.map +0 -1
- package/dist/binary/transform.js +0 -105
- package/dist/binary/transform.js.map +0 -1
- package/dist/bot/adapter.d.ts +0 -5
- package/dist/bot/adapter.d.ts.map +0 -1
- package/dist/bot/adapter.js +0 -11
- package/dist/bot/adapter.js.map +0 -1
- package/dist/bot/bot.d.ts +0 -28
- package/dist/bot/bot.d.ts.map +0 -1
- package/dist/bot/bot.js +0 -52
- package/dist/bot/bot.js.map +0 -1
- package/dist/bot/commands.d.ts +0 -8
- package/dist/bot/commands.d.ts.map +0 -1
- package/dist/bot/commands.js +0 -19
- package/dist/bot/commands.js.map +0 -1
- package/dist/bot/permissions.d.ts +0 -13
- package/dist/bot/permissions.d.ts.map +0 -1
- package/dist/bot/permissions.js +0 -16
- package/dist/bot/permissions.js.map +0 -1
- package/dist/browser/actions.d.ts +0 -33
- package/dist/browser/actions.d.ts.map +0 -1
- package/dist/browser/actions.js +0 -44
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/agent.d.ts +0 -21
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js +0 -13
- package/dist/browser/agent.js.map +0 -1
- package/dist/browser/context.d.ts +0 -43
- package/dist/browser/context.d.ts.map +0 -1
- package/dist/browser/context.js +0 -54
- package/dist/browser/context.js.map +0 -1
- package/dist/browser/fingerprint.d.ts +0 -4
- package/dist/browser/fingerprint.d.ts.map +0 -1
- package/dist/browser/fingerprint.js +0 -12
- package/dist/browser/fingerprint.js.map +0 -1
- package/dist/browser/index.d.ts +0 -11
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -11
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/playwright.d.ts +0 -11
- package/dist/browser/playwright.d.ts.map +0 -1
- package/dist/browser/playwright.js +0 -24
- package/dist/browser/playwright.js.map +0 -1
- package/dist/browser/recorder.d.ts +0 -21
- package/dist/browser/recorder.d.ts.map +0 -1
- package/dist/browser/recorder.js +0 -58
- package/dist/browser/recorder.js.map +0 -1
- package/dist/browser/session.d.ts +0 -14
- package/dist/browser/session.d.ts.map +0 -1
- package/dist/browser/session.js +0 -21
- package/dist/browser/session.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -69
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -133
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/captcha/contract.d.ts +0 -35
- package/dist/captcha/contract.d.ts.map +0 -1
- package/dist/captcha/contract.js +0 -17
- package/dist/captcha/contract.js.map +0 -1
- package/dist/captcha/evidence.d.ts +0 -10
- package/dist/captcha/evidence.d.ts.map +0 -1
- package/dist/captcha/evidence.js +0 -9
- package/dist/captcha/evidence.js.map +0 -1
- package/dist/captcha/guard.d.ts +0 -23
- package/dist/captcha/guard.d.ts.map +0 -1
- package/dist/captcha/guard.js +0 -12
- package/dist/captcha/guard.js.map +0 -1
- package/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -46
- package/dist/cli/main.js.map +0 -1
- package/dist/core/errors.d.ts +0 -84
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -54
- package/dist/core/errors.js.map +0 -1
- package/dist/core/events.d.ts +0 -9
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/events.js.map +0 -1
- package/dist/core/hash.d.ts +0 -10
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -82
- package/dist/core/hash.js.map +0 -1
- package/dist/core/ids.d.ts +0 -10
- package/dist/core/ids.d.ts.map +0 -1
- package/dist/core/ids.js +0 -15
- package/dist/core/ids.js.map +0 -1
- package/dist/delivery/manifest.d.ts +0 -35
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/manifest.js +0 -68
- package/dist/delivery/manifest.js.map +0 -1
- package/dist/deploy/index.d.ts +0 -6
- package/dist/deploy/index.d.ts.map +0 -1
- package/dist/deploy/index.js +0 -6
- package/dist/deploy/index.js.map +0 -1
- package/dist/dispatch/resumable.d.ts +0 -102
- package/dist/dispatch/resumable.d.ts.map +0 -1
- package/dist/dispatch/resumable.js +0 -64
- package/dist/dispatch/resumable.js.map +0 -1
- package/dist/dispatch/workflow.d.ts +0 -6
- package/dist/dispatch/workflow.d.ts.map +0 -1
- package/dist/dispatch/workflow.js +0 -37
- package/dist/dispatch/workflow.js.map +0 -1
- package/dist/domain/artifacts.d.ts +0 -12
- package/dist/domain/artifacts.d.ts.map +0 -1
- package/dist/domain/artifacts.js +0 -14
- package/dist/domain/artifacts.js.map +0 -1
- package/dist/domain/jobs.d.ts +0 -12
- package/dist/domain/jobs.d.ts.map +0 -1
- package/dist/domain/jobs.js +0 -20
- package/dist/domain/jobs.js.map +0 -1
- package/dist/domain/providers.d.ts +0 -10
- package/dist/domain/providers.d.ts.map +0 -1
- package/dist/domain/providers.js +0 -8
- package/dist/domain/providers.js.map +0 -1
- package/dist/domain/runtime.d.ts +0 -14
- package/dist/domain/runtime.d.ts.map +0 -1
- package/dist/domain/runtime.js +0 -10
- package/dist/domain/runtime.js.map +0 -1
- package/dist/domain/sessions.d.ts +0 -12
- package/dist/domain/sessions.d.ts.map +0 -1
- package/dist/domain/sessions.js +0 -57
- package/dist/domain/sessions.js.map +0 -1
- package/dist/examples/localjob.d.ts +0 -2
- package/dist/examples/localjob.d.ts.map +0 -1
- package/dist/examples/localjob.js.map +0 -1
- package/dist/examples/publicapi.d.ts +0 -2
- package/dist/examples/publicapi.d.ts.map +0 -1
- package/dist/examples/publicapi.js.map +0 -1
- package/dist/extension/build.d.ts +0 -9
- package/dist/extension/build.d.ts.map +0 -1
- package/dist/extension/build.js +0 -85
- package/dist/extension/build.js.map +0 -1
- package/dist/extension/content.d.ts +0 -6
- package/dist/extension/content.d.ts.map +0 -1
- package/dist/extension/content.js +0 -135
- package/dist/extension/content.js.map +0 -1
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js +0 -7
- package/dist/extension/index.js.map +0 -1
- package/dist/extension/pagebridge.d.ts +0 -6
- package/dist/extension/pagebridge.d.ts.map +0 -1
- package/dist/extension/pagebridge.js +0 -42
- package/dist/extension/pagebridge.js.map +0 -1
- package/dist/extension/permissions.d.ts +0 -26
- package/dist/extension/permissions.d.ts.map +0 -1
- package/dist/extension/permissions.js +0 -28
- package/dist/extension/permissions.js.map +0 -1
- package/dist/extension/popup.d.ts +0 -5
- package/dist/extension/popup.d.ts.map +0 -1
- package/dist/extension/popup.js +0 -26
- package/dist/extension/popup.js.map +0 -1
- package/dist/extension/protocol.d.ts +0 -58
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js +0 -101
- package/dist/extension/protocol.js.map +0 -1
- package/dist/extension/serviceworker.d.ts +0 -25
- package/dist/extension/serviceworker.d.ts.map +0 -1
- package/dist/extension/serviceworker.js +0 -102
- package/dist/extension/serviceworker.js.map +0 -1
- package/dist/extension/worker.d.ts +0 -28
- package/dist/extension/worker.d.ts.map +0 -1
- package/dist/extension/worker.js +0 -22
- package/dist/extension/worker.js.map +0 -1
- package/dist/format/check.d.ts +0 -8
- package/dist/format/check.d.ts.map +0 -1
- package/dist/format/check.js +0 -38
- package/dist/format/check.js.map +0 -1
- package/dist/isolation/contracts.d.ts.map +0 -1
- package/dist/isolation/contracts.js.map +0 -1
- package/dist/library/public.d.ts.map +0 -1
- package/dist/library/public.js.map +0 -1
- package/dist/mcp/browser.d.ts +0 -9
- package/dist/mcp/browser.d.ts.map +0 -1
- package/dist/mcp/browser.js +0 -13
- package/dist/mcp/browser.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -35
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -40
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/transport.d.ts +0 -7
- package/dist/mcp/transport.d.ts.map +0 -1
- package/dist/mcp/transport.js +0 -17
- package/dist/mcp/transport.js.map +0 -1
- package/dist/memory/bridge.d.ts +0 -14
- package/dist/memory/bridge.d.ts.map +0 -1
- package/dist/memory/bridge.js +0 -16
- package/dist/memory/bridge.js.map +0 -1
- package/dist/memory/engine.d.ts +0 -38
- package/dist/memory/engine.d.ts.map +0 -1
- package/dist/memory/engine.js +0 -109
- package/dist/memory/engine.js.map +0 -1
- package/dist/memory/modes.d.ts +0 -43
- package/dist/memory/modes.d.ts.map +0 -1
- package/dist/memory/modes.js +0 -66
- package/dist/memory/modes.js.map +0 -1
- package/dist/memory/objects.d.ts +0 -21
- package/dist/memory/objects.d.ts.map +0 -1
- package/dist/memory/objects.js +0 -20
- package/dist/memory/objects.js.map +0 -1
- package/dist/memory/planner.d.ts +0 -66
- package/dist/memory/planner.d.ts.map +0 -1
- package/dist/memory/planner.js +0 -108
- package/dist/memory/planner.js.map +0 -1
- package/dist/memory/targets.d.ts +0 -78
- package/dist/memory/targets.d.ts.map +0 -1
- package/dist/memory/targets.js +0 -29
- package/dist/memory/targets.js.map +0 -1
- package/dist/memory/transforms.d.ts +0 -19
- package/dist/memory/transforms.d.ts.map +0 -1
- package/dist/memory/transforms.js +0 -14
- package/dist/memory/transforms.js.map +0 -1
- package/dist/modes/matrix.d.ts +0 -23
- package/dist/modes/matrix.d.ts.map +0 -1
- package/dist/modes/matrix.js +0 -18
- package/dist/modes/matrix.js.map +0 -1
- package/dist/modes/modes.d.ts +0 -33
- package/dist/modes/modes.d.ts.map +0 -1
- package/dist/modes/modes.js +0 -15
- package/dist/modes/modes.js.map +0 -1
- package/dist/modes/resolve.d.ts +0 -96
- package/dist/modes/resolve.d.ts.map +0 -1
- package/dist/modes/resolve.js +0 -48
- package/dist/modes/resolve.js.map +0 -1
- package/dist/observability/metrics.d.ts +0 -14
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js +0 -18
- package/dist/observability/metrics.js.map +0 -1
- package/dist/packager/manifest.d.ts +0 -66
- package/dist/packager/manifest.d.ts.map +0 -1
- package/dist/packager/manifest.js +0 -94
- package/dist/packager/manifest.js.map +0 -1
- package/dist/packager/publish.d.ts +0 -51
- package/dist/packager/publish.d.ts.map +0 -1
- package/dist/packager/publish.js +0 -18
- package/dist/packager/publish.js.map +0 -1
- package/dist/packager/targetcli.d.ts +0 -7
- package/dist/packager/targetcli.d.ts.map +0 -1
- package/dist/packager/targetcli.js +0 -22
- package/dist/packager/targetcli.js.map +0 -1
- package/dist/persistence/adapter.d.ts +0 -5
- package/dist/persistence/adapter.d.ts.map +0 -1
- package/dist/persistence/adapter.js +0 -11
- package/dist/persistence/adapter.js.map +0 -1
- package/dist/persistence/drizzle.d.ts +0 -2
- package/dist/persistence/drizzle.d.ts.map +0 -1
- package/dist/persistence/drizzle.js +0 -12
- package/dist/persistence/drizzle.js.map +0 -1
- package/dist/persistence/memory.d.ts +0 -2
- package/dist/persistence/memory.d.ts.map +0 -1
- package/dist/persistence/memory.js +0 -27
- package/dist/persistence/memory.js.map +0 -1
- package/dist/persistence/migrations.d.ts +0 -19
- package/dist/persistence/migrations.d.ts.map +0 -1
- package/dist/persistence/migrations.js +0 -13
- package/dist/persistence/migrations.js.map +0 -1
- package/dist/persistence/prisma.d.ts +0 -2
- package/dist/persistence/prisma.d.ts.map +0 -1
- package/dist/persistence/prisma.js +0 -25
- package/dist/persistence/prisma.js.map +0 -1
- package/dist/persistence/schema.d.ts +0 -63
- package/dist/persistence/schema.d.ts.map +0 -1
- package/dist/persistence/schema.js +0 -28
- package/dist/persistence/schema.js.map +0 -1
- package/dist/persistence/sql.d.ts +0 -3
- package/dist/persistence/sql.d.ts.map +0 -1
- package/dist/persistence/sql.js +0 -37
- package/dist/persistence/sql.js.map +0 -1
- package/dist/protocol/blocks.d.ts +0 -10
- package/dist/protocol/blocks.d.ts.map +0 -1
- package/dist/protocol/blocks.js +0 -26
- package/dist/protocol/blocks.js.map +0 -1
- package/dist/protocol/json.d.ts +0 -6
- package/dist/protocol/json.d.ts.map +0 -1
- package/dist/protocol/json.js +0 -6
- package/dist/protocol/json.js.map +0 -1
- package/dist/protocol/ndjson.d.ts +0 -3
- package/dist/protocol/ndjson.d.ts.map +0 -1
- package/dist/protocol/ndjson.js +0 -20
- package/dist/protocol/ndjson.js.map +0 -1
- package/dist/protocol/sse.d.ts +0 -7
- package/dist/protocol/sse.d.ts.map +0 -1
- package/dist/protocol/sse.js +0 -25
- package/dist/protocol/sse.js.map +0 -1
- package/dist/proxy/pool.d.ts +0 -9
- package/dist/proxy/pool.d.ts.map +0 -1
- package/dist/proxy/pool.js +0 -27
- package/dist/proxy/pool.js.map +0 -1
- package/dist/queue/idempotency.d.ts +0 -10
- package/dist/queue/idempotency.d.ts.map +0 -1
- package/dist/queue/idempotency.js +0 -13
- package/dist/queue/idempotency.js.map +0 -1
- package/dist/queue/persistent.d.ts +0 -14
- package/dist/queue/persistent.d.ts.map +0 -1
- package/dist/queue/persistent.js +0 -66
- package/dist/queue/persistent.js.map +0 -1
- package/dist/queue/queue.d.ts +0 -7
- package/dist/queue/queue.d.ts.map +0 -1
- package/dist/queue/queue.js +0 -55
- package/dist/queue/queue.js.map +0 -1
- package/dist/queue/saga.d.ts +0 -5
- package/dist/queue/saga.d.ts.map +0 -1
- package/dist/queue/saga.js +0 -20
- package/dist/queue/saga.js.map +0 -1
- package/dist/release/assets.d.ts +0 -144
- package/dist/release/assets.d.ts.map +0 -1
- package/dist/release/assets.js +0 -157
- package/dist/release/assets.js.map +0 -1
- package/dist/release/evidence.d.ts +0 -92
- package/dist/release/evidence.d.ts.map +0 -1
- package/dist/release/evidence.js +0 -148
- package/dist/release/evidence.js.map +0 -1
- package/dist/release/verify.d.ts +0 -12
- package/dist/release/verify.d.ts.map +0 -1
- package/dist/release/verify.js +0 -112
- package/dist/release/verify.js.map +0 -1
- package/dist/runners/chain.d.ts +0 -125
- package/dist/runners/chain.d.ts.map +0 -1
- package/dist/runners/chain.js +0 -95
- package/dist/runners/chain.js.map +0 -1
- package/dist/runners/health.d.ts +0 -36
- package/dist/runners/health.d.ts.map +0 -1
- package/dist/runners/health.js +0 -26
- package/dist/runners/health.js.map +0 -1
- package/dist/runners/heartbeat.d.ts +0 -23
- package/dist/runners/heartbeat.d.ts.map +0 -1
- package/dist/runners/heartbeat.js +0 -29
- package/dist/runners/heartbeat.js.map +0 -1
- package/dist/runners/inprocess.d.ts +0 -17
- package/dist/runners/inprocess.d.ts.map +0 -1
- package/dist/runners/inprocess.js +0 -20
- package/dist/runners/inprocess.js.map +0 -1
- package/dist/runners/scheduler.d.ts +0 -5
- package/dist/runners/scheduler.d.ts.map +0 -1
- package/dist/runners/scheduler.js +0 -19
- package/dist/runners/scheduler.js.map +0 -1
- package/dist/runtime/abort.d.ts +0 -8
- package/dist/runtime/abort.d.ts.map +0 -1
- package/dist/runtime/abort.js +0 -13
- package/dist/runtime/abort.js.map +0 -1
- package/dist/runtime/compatibility.d.ts +0 -37
- package/dist/runtime/compatibility.d.ts.map +0 -1
- package/dist/runtime/compatibility.js +0 -10
- package/dist/runtime/compatibility.js.map +0 -1
- package/dist/runtime/detect.d.ts +0 -13
- package/dist/runtime/detect.d.ts.map +0 -1
- package/dist/runtime/detect.js +0 -18
- package/dist/runtime/detect.js.map +0 -1
- package/dist/runtime/engine.d.ts +0 -20
- package/dist/runtime/engine.d.ts.map +0 -1
- package/dist/runtime/engine.js +0 -61
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/retry.d.ts +0 -19
- package/dist/runtime/retry.d.ts.map +0 -1
- package/dist/runtime/retry.js +0 -54
- package/dist/runtime/retry.js.map +0 -1
- package/dist/runtime/worker.d.ts +0 -10
- package/dist/runtime/worker.d.ts.map +0 -1
- package/dist/runtime/worker.js +0 -25
- package/dist/runtime/worker.js.map +0 -1
- package/dist/scrape/cache.d.ts +0 -45
- package/dist/scrape/cache.d.ts.map +0 -1
- package/dist/scrape/cache.js +0 -45
- package/dist/scrape/cache.js.map +0 -1
- package/dist/scrape/crawl.d.ts +0 -47
- package/dist/scrape/crawl.d.ts.map +0 -1
- package/dist/scrape/crawl.js +0 -114
- package/dist/scrape/crawl.js.map +0 -1
- package/dist/scrape/extract.d.ts +0 -63
- package/dist/scrape/extract.d.ts.map +0 -1
- package/dist/scrape/extract.js +0 -47
- package/dist/scrape/extract.js.map +0 -1
- package/dist/scrape/normalize.d.ts +0 -52
- package/dist/scrape/normalize.d.ts.map +0 -1
- package/dist/scrape/normalize.js +0 -102
- package/dist/scrape/normalize.js.map +0 -1
- package/dist/scrape/robots.d.ts +0 -37
- package/dist/scrape/robots.d.ts.map +0 -1
- package/dist/scrape/robots.js +0 -71
- package/dist/scrape/robots.js.map +0 -1
- package/dist/scrape/schema.d.ts +0 -23
- package/dist/scrape/schema.d.ts.map +0 -1
- package/dist/scrape/schema.js +0 -92
- package/dist/scrape/schema.js.map +0 -1
- package/dist/scrape/scraper.d.ts +0 -6
- package/dist/scrape/scraper.d.ts.map +0 -1
- package/dist/scrape/scraper.js +0 -46
- package/dist/scrape/scraper.js.map +0 -1
- package/dist/scrape/semantic.d.ts +0 -25
- package/dist/scrape/semantic.d.ts.map +0 -1
- package/dist/scrape/semantic.js +0 -27
- package/dist/scrape/semantic.js.map +0 -1
- package/dist/server/node.d.ts +0 -7
- package/dist/server/node.d.ts.map +0 -1
- package/dist/server/node.js.map +0 -1
- package/dist/sessions/file.d.ts +0 -25
- package/dist/sessions/file.d.ts.map +0 -1
- package/dist/sessions/file.js +0 -13
- package/dist/sessions/file.js.map +0 -1
- package/dist/sessions/replay.d.ts +0 -9
- package/dist/sessions/replay.d.ts.map +0 -1
- package/dist/sessions/replay.js +0 -70
- package/dist/sessions/replay.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -25
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -13
- package/dist/sessions/store.js.map +0 -1
- package/dist/storage/adapter.d.ts +0 -5
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js +0 -11
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/cache.d.ts +0 -23
- package/dist/storage/cache.d.ts.map +0 -1
- package/dist/storage/cache.js +0 -79
- package/dist/storage/cache.js.map +0 -1
- package/dist/storage/checksum.d.ts +0 -3
- package/dist/storage/checksum.d.ts.map +0 -1
- package/dist/storage/checksum.js +0 -19
- package/dist/storage/checksum.js.map +0 -1
- package/dist/storage/chunked.d.ts +0 -21
- package/dist/storage/chunked.d.ts.map +0 -1
- package/dist/storage/chunked.js +0 -67
- package/dist/storage/chunked.js.map +0 -1
- package/dist/storage/content.d.ts +0 -24
- package/dist/storage/content.d.ts.map +0 -1
- package/dist/storage/content.js +0 -44
- package/dist/storage/content.js.map +0 -1
- package/dist/storage/filehosting.d.ts +0 -2
- package/dist/storage/filehosting.d.ts.map +0 -1
- package/dist/storage/filehosting.js +0 -24
- package/dist/storage/filehosting.js.map +0 -1
- package/dist/storage/githubcontents.d.ts +0 -2
- package/dist/storage/githubcontents.d.ts.map +0 -1
- package/dist/storage/githubcontents.js +0 -29
- package/dist/storage/githubcontents.js.map +0 -1
- package/dist/storage/index.d.ts +0 -12
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -12
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/local.d.ts +0 -2
- package/dist/storage/local.d.ts.map +0 -1
- package/dist/storage/local.js +0 -72
- package/dist/storage/local.js.map +0 -1
- package/dist/storage/memory.d.ts +0 -6
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js +0 -36
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/pool.d.ts +0 -95
- package/dist/storage/pool.d.ts.map +0 -1
- package/dist/storage/pool.js +0 -202
- package/dist/storage/pool.js.map +0 -1
- package/dist/storage/s3compatible.d.ts +0 -3
- package/dist/storage/s3compatible.d.ts.map +0 -1
- package/dist/storage/s3compatible.js +0 -70
- package/dist/storage/s3compatible.js.map +0 -1
- package/dist/storage/sync.d.ts +0 -47
- package/dist/storage/sync.d.ts.map +0 -1
- package/dist/storage/sync.js +0 -75
- package/dist/storage/sync.js.map +0 -1
- package/dist/surfaces/adapters.d.ts +0 -97
- package/dist/surfaces/adapters.d.ts.map +0 -1
- package/dist/surfaces/adapters.js +0 -50
- package/dist/surfaces/adapters.js.map +0 -1
- package/dist/surfaces/controls.d.ts +0 -18
- package/dist/surfaces/controls.d.ts.map +0 -1
- package/dist/surfaces/controls.js +0 -45
- package/dist/surfaces/controls.js.map +0 -1
- package/dist/surfaces/manifest.d.ts +0 -68
- package/dist/surfaces/manifest.d.ts.map +0 -1
- package/dist/surfaces/manifest.js +0 -42
- package/dist/surfaces/manifest.js.map +0 -1
- package/dist/surfaces/n8n.d.ts +0 -25
- package/dist/surfaces/n8n.d.ts.map +0 -1
- package/dist/surfaces/n8n.js +0 -31
- package/dist/surfaces/n8n.js.map +0 -1
- package/dist/surfaces/operations.d.ts +0 -32
- package/dist/surfaces/operations.d.ts.map +0 -1
- package/dist/surfaces/operations.js +0 -53
- package/dist/surfaces/operations.js.map +0 -1
- package/dist/surfaces/requirements.d.ts +0 -29
- package/dist/surfaces/requirements.d.ts.map +0 -1
- package/dist/surfaces/requirements.js +0 -50
- package/dist/surfaces/requirements.js.map +0 -1
- package/dist/surfaces/targets.d.ts +0 -196
- package/dist/surfaces/targets.d.ts.map +0 -1
- package/dist/surfaces/targets.js +0 -27
- package/dist/surfaces/targets.js.map +0 -1
- package/dist/webhook/delivery.d.ts +0 -18
- package/dist/webhook/delivery.d.ts.map +0 -1
- package/dist/webhook/delivery.js +0 -36
- package/dist/webhook/delivery.js.map +0 -1
- package/dist/webhook/receiver.d.ts +0 -26
- package/dist/webhook/receiver.d.ts.map +0 -1
- package/dist/webhook/receiver.js +0 -24
- package/dist/webhook/receiver.js.map +0 -1
- package/dist/webhook/signature.d.ts +0 -3
- package/dist/webhook/signature.d.ts.map +0 -1
- package/dist/webhook/signature.js +0 -7
- package/dist/webhook/signature.js.map +0 -1
- package/dist/workflow/manifest.d.ts +0 -22
- package/dist/workflow/manifest.d.ts.map +0 -1
- package/dist/workflow/manifest.js +0 -21
- package/dist/workflow/manifest.js.map +0 -1
- package/dist/workflow/registry.d.ts +0 -7
- package/dist/workflow/registry.d.ts.map +0 -1
- package/dist/workflow/registry.js +0 -18
- package/dist/workflow/registry.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -6
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -18
- package/dist/workflow/templates.js.map +0 -1
- package/dist/workflow/triggers.d.ts +0 -64
- package/dist/workflow/triggers.d.ts.map +0 -1
- package/dist/workflow/triggers.js +0 -99
- package/dist/workflow/triggers.js.map +0 -1
- /package/{extension/README.md → docs/extension.md} +0 -0
package/gpumonitor.cpp
ADDED
|
@@ -0,0 +1,1543 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* gpumonitor.cpp — gpu identity spoofing suite for the virtual hardware
|
|
3
|
+
* engine v5 (vhe), written against c++26.
|
|
4
|
+
*
|
|
5
|
+
* one translation unit, four build modes (see the dockerfile):
|
|
6
|
+
* 1. CLI (-DVHE_BUILD_CLI) the virtual nvidia-smi adapter binary installed at
|
|
7
|
+
* /usr/local/bin/nvidia-smi; plain table,
|
|
8
|
+
* --query-gpu csv mode and -L device list,
|
|
9
|
+
* same surface as pogusthewhisper/fake-nvidia-smi.
|
|
10
|
+
* 2. SHIM (-DVHE_BUILD_PRELOAD) shared object exporting nvml* and cu*
|
|
11
|
+
* symbols; when preloaded (or symlinked as
|
|
12
|
+
* libnvidia-ml.so.1 / libcuda.so.1) it
|
|
13
|
+
* forwards to a real driver found after this
|
|
14
|
+
* object via dlsym(RTLD_NEXT, ...) and
|
|
15
|
+
* synthesizes data otherwise — the unified
|
|
16
|
+
* shim pattern from rick-hsu/nvml-unified-shim
|
|
17
|
+
* plus the libcuda entry-point interception
|
|
18
|
+
* documented by FanBB2333/GpuAdapter.
|
|
19
|
+
* 3. ADDON (-DVHE_BUILD_ADDON) node native addon (node_api.h, node 26.7.0,
|
|
20
|
+
* NODE_MODULE_VERSION 147) exporting the
|
|
21
|
+
* table generator to javascript.
|
|
22
|
+
* 4. FORGE (-DVHE_BUILD_FORGE) the aetherforge control plane (v2 merge of
|
|
23
|
+
* forge.cpp/forge.hpp): plan validation, the
|
|
24
|
+
* qemu 11.1.0 argv generator with hugepages,
|
|
25
|
+
* virtio-mem, swtpm tpm-crb, looking glass
|
|
26
|
+
* ivshmem and whpx accel, plus the mttg
|
|
27
|
+
* work-stealing grid model. the planner is
|
|
28
|
+
* the c++ mirror of the architect planner in
|
|
29
|
+
* index.ts and carries the flags the ts side
|
|
30
|
+
* leaves to operators.
|
|
31
|
+
*
|
|
32
|
+
* hardware numbers below were verified on 22/08/2026 against vendor pages
|
|
33
|
+
* and the worklog task 1-d research:
|
|
34
|
+
* NVIDIA-SMI 575.57.08 / CUDA 12.9 header (fake-nvidia-smi confirmed)
|
|
35
|
+
* A100-SXM4-40GB 40536 MiB 400 W
|
|
36
|
+
* H100-SXM5-80GB 81559 MiB 700 W
|
|
37
|
+
* GeForce RTX 5090 32768 MiB 575 W
|
|
38
|
+
* RTX PRO 6000 97887 MiB 600 W (blackwell workstation, 96 GB)
|
|
39
|
+
* B200 196608 MiB 1000 W (192 GB HBM3e)
|
|
40
|
+
*
|
|
41
|
+
* c++26 features exercised deliberately, each with a toolchain fallback:
|
|
42
|
+
* - import std; primary path; -DVHE_NO_MODULES switches
|
|
43
|
+
* to classic includes for libstdc++
|
|
44
|
+
* toolchains without the std module.
|
|
45
|
+
* - std::expected (p0323r12) every renderer returns
|
|
46
|
+
* expected<string, spooferror> instead of
|
|
47
|
+
* throwing, so the CLI and the napi addon
|
|
48
|
+
* surface errors as values.
|
|
49
|
+
* - std::mdspan (p0009r18) the spec matrix is a 2D non-owning view,
|
|
50
|
+
* extended sizes, operator() access.
|
|
51
|
+
* - pack indexing ts...[i] default profile selection at compile time
|
|
52
|
+
* (p2662r3, c++26); __cpp_pack_indexing
|
|
53
|
+
* guards a std::forward_as_tuple fallback
|
|
54
|
+
* for clang-19/gcc-14 pre-adoption builds.
|
|
55
|
+
* - std::print / std::format all console output; no printf anywhere.
|
|
56
|
+
*
|
|
57
|
+
* environment variables consumed (all optional):
|
|
58
|
+
* VHE_GPU_PROFILE profile key: a100 (default), h100, rtx5090, rtxpro6000,
|
|
59
|
+
* b200; aliases: 5090, pro6000, a100-40gb, h100-80gb
|
|
60
|
+
* VHE_GPUS gpu count reported (default 8, DGX-class node)
|
|
61
|
+
* VHE_SMI_DRIVER driver version string (default 575.57.08)
|
|
62
|
+
* VHE_SMI_CUDA cuda version string (default 12.9)
|
|
63
|
+
* VHE_MIG 1 renders a MIG instance table inside the output
|
|
64
|
+
* VHE_DEBUG 1 prints interception decisions to stderr
|
|
65
|
+
*
|
|
66
|
+
* build (shim): clang++-19 -std=c++26 -O3 -march=native -flto -shared -fPIC
|
|
67
|
+
* -ldl gpumonitor.cpp -o libgpumonitor.so
|
|
68
|
+
* build (cli): clang++-19 -std=c++26 -O3 -march=native -flto
|
|
69
|
+
* -DVHE_BUILD_CLI gpumonitor.cpp -o nvidia-smi
|
|
70
|
+
* build (addon): clang++-19 -std=c++26 -O3 -shared -fPIC -DVHE_BUILD_ADDON
|
|
71
|
+
* -DNODE_GYP_MODULE_NAME=vhe_gpu -I/usr/include/node
|
|
72
|
+
* gpumonitor.cpp -o vhe_gpu.node
|
|
73
|
+
* build (forge): clang++-19 -std=c++26 -O3 -DVHE_BUILD_FORGE
|
|
74
|
+
* gpumonitor.cpp -o aetherforge
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
#ifdef VHE_NO_MODULES /* classic include path for toolchains without std module */
|
|
78
|
+
#include <algorithm>
|
|
79
|
+
#include <array>
|
|
80
|
+
#include <chrono>
|
|
81
|
+
#include <cstdint>
|
|
82
|
+
#include <cstdio>
|
|
83
|
+
#include <cstdlib>
|
|
84
|
+
#include <expected>
|
|
85
|
+
#include <format>
|
|
86
|
+
#include <optional>
|
|
87
|
+
#include <print>
|
|
88
|
+
#include <sstream>
|
|
89
|
+
#include <string>
|
|
90
|
+
#include <string_view>
|
|
91
|
+
#include <thread>
|
|
92
|
+
#include <tuple>
|
|
93
|
+
#include <utility>
|
|
94
|
+
#include <vector>
|
|
95
|
+
#if __has_include(<mdspan>)
|
|
96
|
+
#include <mdspan>
|
|
97
|
+
#define VHEHAVESTDMDSPAN 1
|
|
98
|
+
#endif
|
|
99
|
+
#else /* c++26 named module: the whole standard library in one import */
|
|
100
|
+
import std;
|
|
101
|
+
#define VHEHAVESTDMDSPAN 1
|
|
102
|
+
#endif
|
|
103
|
+
|
|
104
|
+
#include <dlfcn.h> /* posix dlsym/RTLD_NEXT, outside the std module */
|
|
105
|
+
|
|
106
|
+
#ifdef VHE_BUILD_ADDON
|
|
107
|
+
#include <node_api.h> /* node 26 stable n-api, abi stable across versions */
|
|
108
|
+
#endif
|
|
109
|
+
|
|
110
|
+
namespace vhe {
|
|
111
|
+
|
|
112
|
+
/* ------------------------------------------------------------------ */
|
|
113
|
+
/* constants and real hardware specs */
|
|
114
|
+
/* ------------------------------------------------------------------ */
|
|
115
|
+
|
|
116
|
+
/** summary table geometry: the real nvidia-smi frame is 89 columns wide;
|
|
117
|
+
* segment widths are 41 | 23 | 21 inner columns plus the four border
|
|
118
|
+
* characters, matching the 575.x layout byte for byte where it matters. */
|
|
119
|
+
inline constexpr std::size_t ksegleft = 41;
|
|
120
|
+
inline constexpr std::size_t ksegmid = 23;
|
|
121
|
+
inline constexpr std::size_t ksegright = 21;
|
|
122
|
+
inline constexpr std::size_t kinner = 87; /* 41+23+21+2 separators */
|
|
123
|
+
inline constexpr std::size_t ktable = 89;
|
|
124
|
+
|
|
125
|
+
/** driver and cuda identity printed in the header row (fake-nvidia-smi
|
|
126
|
+
* confirmed this exact pair for 2026 datacenter branches). */
|
|
127
|
+
inline constexpr std::string_view kdriverdefault = "575.57.08";
|
|
128
|
+
inline constexpr std::string_view kcudadefault = "12.9";
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* one gpu product entry. every numeric field comes from the vendor pages
|
|
132
|
+
* verified in worklog task 1-d; the values are exactly what a real
|
|
133
|
+
* nvidia-smi would print on the corresponding hardware.
|
|
134
|
+
*/
|
|
135
|
+
struct gpuspec {
|
|
136
|
+
std::string_view key; /* profile lookup key */
|
|
137
|
+
std::string_view name; /* marketing name shown in the table */
|
|
138
|
+
std::string_view arch; /* architecture tag for -L output */
|
|
139
|
+
unsigned long long totalmib; /* vram as nvidia-smi reports it (MiB) */
|
|
140
|
+
unsigned powerw; /* enforced power cap */
|
|
141
|
+
unsigned tempc; /* healthy idle-ish temperature */
|
|
142
|
+
unsigned fanpct; /* fan percentage (datacenter: N/A) */
|
|
143
|
+
unsigned utilpct; /* steady-state utilization */
|
|
144
|
+
unsigned pciegen; /* generation for bus metadata */
|
|
145
|
+
unsigned mig; /* 1 = MIG capable (A100/H100/B200) */
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
inline constexpr gpuspec speca100{
|
|
149
|
+
"a100", "NVIDIA A100-SXM4-40GB", "ampere", 40536, 400, 45, 0, 97, 4, 1};
|
|
150
|
+
inline constexpr gpuspec spech100{
|
|
151
|
+
"h100", "NVIDIA H100-SXM5-80GB", "hopper", 81559, 700, 43, 0, 99, 5, 1};
|
|
152
|
+
inline constexpr gpuspec specrtx5090{
|
|
153
|
+
"rtx5090", "NVIDIA GeForce RTX 5090", "blackwell-geforce", 32768, 575,
|
|
154
|
+
41, 30, 96, 5, 0};
|
|
155
|
+
inline constexpr gpuspec specrtxpro6000{
|
|
156
|
+
"rtxpro6000", "NVIDIA RTX PRO 6000", "blackwell-workstation", 97887, 600,
|
|
157
|
+
47, 32, 94, 5, 1};
|
|
158
|
+
inline constexpr gpuspec specb200{
|
|
159
|
+
"b200", "NVIDIA B200", "blackwell-datacenter", 196608, 1000, 55, 0, 98, 6,
|
|
160
|
+
1};
|
|
161
|
+
|
|
162
|
+
/** flat spec matrix consumed through std::mdspan below. column order:
|
|
163
|
+
* 0 totalmib, 1 powerw, 2 tempc, 3 fanpct, 4 utilpct, 5 pciegen,
|
|
164
|
+
* 6 mig, 7 bar1mib. rows follow the constexpr spec declaration order. */
|
|
165
|
+
inline constexpr std::array<unsigned, 8 * 5> kspecflat{
|
|
166
|
+
40536, 400, 45, 0, 97, 4, 1, 32768, /* a100 */
|
|
167
|
+
81559, 700, 43, 0, 99, 5, 1, 131072, /* h100 */
|
|
168
|
+
32768, 575, 41, 30, 96, 5, 0, 32768, /* rtx 5090 */
|
|
169
|
+
97887, 600, 47, 32, 94, 5, 1, 131072, /* rtx pro 6000*/
|
|
170
|
+
196608, 1000, 55, 0, 98, 6, 1, 262144, /* b200 */
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* mdspan over the flat matrix. the c++23 std::mdspan view (p0009r18, adopted
|
|
175
|
+
* into c++26 toolchains as a core library piece) gives bounds-checked-free
|
|
176
|
+
* 2D indexing without owning storage; on toolchains without <mdspan> the
|
|
177
|
+
* fallback keeps the same operator() surface so call sites never change.
|
|
178
|
+
*/
|
|
179
|
+
#if defined(VHEHAVESTDMDSPAN)
|
|
180
|
+
inline constexpr auto kspecs =
|
|
181
|
+
std::mdspan<const unsigned, std::extents<std::size_t, 5, 8>>(
|
|
182
|
+
kspecflat.data());
|
|
183
|
+
#else
|
|
184
|
+
/** minimal fixed-extent 2D view mirroring std::mdspan's call syntax. */
|
|
185
|
+
template <class T, std::size_t Rows, std::size_t Cols>
|
|
186
|
+
struct compatmdspan {
|
|
187
|
+
const T* mdata;
|
|
188
|
+
constexpr const T& operator()(std::size_t r, std::size_t c) const {
|
|
189
|
+
return mdata[r * Cols + c];
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
inline constexpr compatmdspan<const unsigned, 5, 8> kspecs{
|
|
193
|
+
kspecflat.data()};
|
|
194
|
+
#endif
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* returns the index-th element of a function parameter pack.
|
|
198
|
+
* primary path uses c++26 pack indexing (p2662r3, `ts...[index]`); the
|
|
199
|
+
* fallback covers gcc-14 and clang-19 libstdc++ builds where the feature is
|
|
200
|
+
* not yet enabled, via std::forward_as_tuple + std::get.
|
|
201
|
+
* @param index compile-time position within the pack
|
|
202
|
+
* @param ts the pack elements
|
|
203
|
+
* @return reference to the selected element
|
|
204
|
+
*/
|
|
205
|
+
#if defined(__cpp_pack_indexing)
|
|
206
|
+
template <std::size_t index, typename... ts>
|
|
207
|
+
[[nodiscard]] constexpr decltype(auto) packat(ts&&... tspack) noexcept {
|
|
208
|
+
return tspack...[index]; /* c++26 pack indexing */
|
|
209
|
+
}
|
|
210
|
+
#else
|
|
211
|
+
template <std::size_t index, typename... ts>
|
|
212
|
+
[[nodiscard]] constexpr decltype(auto) packat(ts&&... tspack) noexcept {
|
|
213
|
+
return std::get<index>(std::forward_as_tuple(tspack...));
|
|
214
|
+
}
|
|
215
|
+
#endif
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* compile-time default profile selection through pack indexing: the whole
|
|
219
|
+
* chain from kspecs back to constexpr spec objects evaluates at compile
|
|
220
|
+
* time, which the static_asserts below prove.
|
|
221
|
+
* @param idx profile index, 0=a100 .. 4=b200
|
|
222
|
+
* @return reference to the constexpr gpuspec
|
|
223
|
+
*/
|
|
224
|
+
template <std::size_t idx>
|
|
225
|
+
[[nodiscard]] constexpr const gpuspec& defaultprofile() {
|
|
226
|
+
return packat<idx>(speca100, spech100, specrtx5090, specrtxpro6000,
|
|
227
|
+
specb200);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static_assert(defaultprofile<0>().totalmib == 40536,
|
|
231
|
+
"pack-indexed a100 profile must carry 40536 MiB");
|
|
232
|
+
static_assert(defaultprofile<4>().powerw == 1000,
|
|
233
|
+
"pack-indexed b200 profile must carry the 1000 W cap");
|
|
234
|
+
static_assert(kspecs(4, 0) == 196608, "mdspan row b200 must carry 196608 MiB");
|
|
235
|
+
static_assert(ktable == 2 + kinner, "table frame math must stay consistent");
|
|
236
|
+
|
|
237
|
+
/* ------------------------------------------------------------------ */
|
|
238
|
+
/* error handling: std::expected everywhere, no exceptions */
|
|
239
|
+
/* ------------------------------------------------------------------ */
|
|
240
|
+
|
|
241
|
+
/** error taxonomy for the renderers; codes mirror nvidia-smi exit reasons. */
|
|
242
|
+
enum class spooferrc {
|
|
243
|
+
unknownprofile, /**< profile key not in the catalog */
|
|
244
|
+
invalidcount, /**< gpu count outside [1, 256] */
|
|
245
|
+
formatfailure, /**< a row exceeded its fixed-width segment */
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/** rich error value carried by std::expected. */
|
|
249
|
+
struct spooferror {
|
|
250
|
+
spooferrc code;
|
|
251
|
+
std::string detail;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/** result type used by every renderer in this translation unit. */
|
|
255
|
+
using smiresult = std::expected<std::string, spooferror>;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* builds a spooferror with a formatted message.
|
|
259
|
+
* @param code error category
|
|
260
|
+
* @param fmt std::format style message
|
|
261
|
+
* @return the fully populated error value
|
|
262
|
+
*/
|
|
263
|
+
[[nodiscard]] inline spooferror makeerror(spooferrc code,
|
|
264
|
+
std::string_view fmt) {
|
|
265
|
+
return spooferror{code, std::string{fmt}};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* ------------------------------------------------------------------ */
|
|
269
|
+
/* small text helpers */
|
|
270
|
+
/* ------------------------------------------------------------------ */
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* truncates or left-pads a string to an exact segment width so the 89-column
|
|
274
|
+
* frame can never be broken by a long gpu name.
|
|
275
|
+
* @param s input text
|
|
276
|
+
* @param width target width
|
|
277
|
+
* @return exactly width characters
|
|
278
|
+
*/
|
|
279
|
+
[[nodiscard]] inline std::string fit(std::string_view s, std::size_t width) {
|
|
280
|
+
std::string out{s.substr(0, width)};
|
|
281
|
+
if (out.size() < width)
|
|
282
|
+
out.append(width - out.size(), ' ');
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** right-aligned variant of fit(), used for numeric columns. */
|
|
287
|
+
[[nodiscard]] inline std::string fitr(std::string_view s,
|
|
288
|
+
std::size_t width) {
|
|
289
|
+
std::string out{s.substr(0, width)};
|
|
290
|
+
if (out.size() < width)
|
|
291
|
+
out.insert(0, width - out.size(), ' ');
|
|
292
|
+
return out;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* fnv-1a 64-bit hash; produces stable pseudo-random-looking identifiers so
|
|
297
|
+
* bus-ids and uuids survive repeated invocations (tools diff two samples).
|
|
298
|
+
* @param data bytes to hash
|
|
299
|
+
* @param seed mixing seed (gpu index, process pid, ...)
|
|
300
|
+
* @return 64-bit digest
|
|
301
|
+
*/
|
|
302
|
+
[[nodiscard]] constexpr std::uint64_t fnv1a(std::string_view data,
|
|
303
|
+
std::uint64_t seed) {
|
|
304
|
+
std::uint64_t h = 0xcbf29ce484222325ULL ^ seed;
|
|
305
|
+
for (char c : data) {
|
|
306
|
+
h ^= static_cast<std::uint64_t>(static_cast<unsigned char>(c));
|
|
307
|
+
h *= 0x100000001b3ULL;
|
|
308
|
+
}
|
|
309
|
+
return h;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* formats a digest as the nvidia uuid v4 shape used by nvidia-smi -L:
|
|
314
|
+
* GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
|
315
|
+
* @param h digest bits
|
|
316
|
+
* @return 40-character uuid string starting with "GPU-"
|
|
317
|
+
*/
|
|
318
|
+
[[nodiscard]] inline std::string gpuuuid(std::uint64_t h) {
|
|
319
|
+
return std::format("GPU-{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
|
|
320
|
+
static_cast<unsigned>(h >> 32),
|
|
321
|
+
static_cast<unsigned>((h >> 16) & 0xffff),
|
|
322
|
+
static_cast<unsigned>(h & 0xffff),
|
|
323
|
+
static_cast<unsigned>((h >> 48) & 0xffff),
|
|
324
|
+
h & 0xffffffffffffULL);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** reads an optional environment string; empty or unset yields nullopt. */
|
|
328
|
+
[[nodiscard]] inline std::optional<std::string> envstr(
|
|
329
|
+
const char* name) {
|
|
330
|
+
const char* raw = std::getenv(name);
|
|
331
|
+
if (raw == nullptr || *raw == '\0')
|
|
332
|
+
return std::nullopt;
|
|
333
|
+
return std::string{raw};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** reads an optional environment number with clamping, default fallback. */
|
|
337
|
+
[[nodiscard]] inline unsigned envuint(const char* name, unsigned fallback,
|
|
338
|
+
unsigned lo, unsigned hi) {
|
|
339
|
+
auto raw = envstr(name);
|
|
340
|
+
if (!raw)
|
|
341
|
+
return fallback;
|
|
342
|
+
unsigned value = 0;
|
|
343
|
+
try {
|
|
344
|
+
value = static_cast<unsigned>(std::stoul(*raw));
|
|
345
|
+
} catch (...) {
|
|
346
|
+
return fallback; /* malformed input keeps the default, never throws */
|
|
347
|
+
}
|
|
348
|
+
return value < lo ? lo : (value > hi ? hi : value);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* resolves a profile key to its spec, accepting the documented aliases.
|
|
353
|
+
* @param key profile name or alias
|
|
354
|
+
* @return pointer to the constexpr spec, or an error value
|
|
355
|
+
*/
|
|
356
|
+
[[nodiscard]] smiresult resolveprofile(std::string_view key) {
|
|
357
|
+
if (key == "a100" || key == "a100-40gb" || key == "A100")
|
|
358
|
+
return std::string{speca100.name};
|
|
359
|
+
if (key == "h100" || key == "h100-80gb" || key == "H100")
|
|
360
|
+
return std::string{spech100.name};
|
|
361
|
+
if (key == "rtx5090" || key == "5090" || key == "RTX5090")
|
|
362
|
+
return std::string{specrtx5090.name};
|
|
363
|
+
if (key == "rtxpro6000" || key == "pro6000" || key == "RTXPRO6000")
|
|
364
|
+
return std::string{specrtxpro6000.name};
|
|
365
|
+
if (key == "b200" || key == "B200")
|
|
366
|
+
return std::string{specb200.name};
|
|
367
|
+
return std::unexpected(makeerror(
|
|
368
|
+
spooferrc::unknownprofile,
|
|
369
|
+
std::format("unknown profile '{}': expected a100, h100, rtx5090, "
|
|
370
|
+
"rtxpro6000 or b200", key)));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* finds the full gpuspec for a resolved key. resolveprofile returns the
|
|
375
|
+
* name; this helper maps names back to specs through the flat catalog.
|
|
376
|
+
* @param key profile key
|
|
377
|
+
* @return spec pointer or error
|
|
378
|
+
*/
|
|
379
|
+
[[nodiscard]] std::expected<const gpuspec*, spooferror> findspec(
|
|
380
|
+
std::string_view key) {
|
|
381
|
+
const gpuspec* catalog[] = {&speca100, &spech100, &specrtx5090,
|
|
382
|
+
&specrtxpro6000, &specb200};
|
|
383
|
+
for (const gpuspec* spec : catalog)
|
|
384
|
+
if (spec->key == key)
|
|
385
|
+
return spec;
|
|
386
|
+
return std::unexpected(makeerror(
|
|
387
|
+
spooferrc::unknownprofile,
|
|
388
|
+
std::format("profile '{}' not present in the constexpr catalog", key)));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* ------------------------------------------------------------------ */
|
|
392
|
+
/* the virtual nvidia-smi adapter renderer */
|
|
393
|
+
/* ------------------------------------------------------------------ */
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* renders the complete nvidia-smi summary: asctime timestamp, 89-column
|
|
397
|
+
* frame, header row, one block per gpu (identity row + telemetry row),
|
|
398
|
+
* processes section, and — when VHE_MIG=1 — a MIG instance table.
|
|
399
|
+
* memory-used derives deterministically from fnv1a(name, index) so the
|
|
400
|
+
* output is stable within a process but distinct per gpu, mirroring how
|
|
401
|
+
* fake-nvidia-smi derives its realistic telemetry.
|
|
402
|
+
* @param profile profile key ("a100", "h100", ...)
|
|
403
|
+
* @param gpucount number of devices to report
|
|
404
|
+
* @param driver driver version for the header (default 575.57.08)
|
|
405
|
+
* @param cuda cuda version for the header (default 12.9)
|
|
406
|
+
* @param mig 1 renders MIG instances
|
|
407
|
+
* @return the full table text or a spooferror
|
|
408
|
+
*/
|
|
409
|
+
[[nodiscard]] smiresult rendertable(std::string_view profile,
|
|
410
|
+
unsigned gpucount,
|
|
411
|
+
std::string_view driver,
|
|
412
|
+
std::string_view cuda, bool mig) {
|
|
413
|
+
if (gpucount == 0 || gpucount > 256)
|
|
414
|
+
return std::unexpected(makeerror(
|
|
415
|
+
spooferrc::invalidcount,
|
|
416
|
+
std::format("gpu count {} outside the supported [1, 256] range",
|
|
417
|
+
gpucount)));
|
|
418
|
+
|
|
419
|
+
auto specorerr = findspec(profile);
|
|
420
|
+
if (!specorerr)
|
|
421
|
+
return std::unexpected(specorerr.error());
|
|
422
|
+
const gpuspec& spec = **specorerr;
|
|
423
|
+
|
|
424
|
+
const std::string fullborder = "+" + std::string(kinner, '-') + "+";
|
|
425
|
+
const std::string segborder =
|
|
426
|
+
"|" + std::string(ksegleft, '-') + "+" +
|
|
427
|
+
std::string(ksegmid, '-') + "+" + std::string(ksegright, '-') +
|
|
428
|
+
"|";
|
|
429
|
+
const std::string segheavy =
|
|
430
|
+
"|" + std::string(ksegleft, '=') + "+" +
|
|
431
|
+
std::string(ksegmid, '=') + "+" + std::string(ksegright, '=') +
|
|
432
|
+
"|";
|
|
433
|
+
|
|
434
|
+
std::string out;
|
|
435
|
+
out.reserve(1024 + gpucount * 180);
|
|
436
|
+
|
|
437
|
+
/* timestamp exactly as asctime/nvidia-smi print it: %a %b %e %H:%M:%S %Y */
|
|
438
|
+
const auto now = std::chrono::floor<std::chrono::seconds>(
|
|
439
|
+
std::chrono::system_clock::now());
|
|
440
|
+
out += std::format("{:%a %b %e %H:%M:%S %Y}\n", now);
|
|
441
|
+
out += fullborder + "\n";
|
|
442
|
+
out += "|" +
|
|
443
|
+
fit(std::format(" NVIDIA-SMI {} Driver Version: {} "
|
|
444
|
+
"CUDA Version: {}",
|
|
445
|
+
driver, driver, cuda),
|
|
446
|
+
kinner) +
|
|
447
|
+
"|\n";
|
|
448
|
+
out += segborder + "\n";
|
|
449
|
+
out += "|" + fit(" GPU Name Driver-Model", ksegleft) +
|
|
450
|
+
"|" + fit(" Bus-Id Disp.A", ksegmid) + "|" +
|
|
451
|
+
fit(" Volatile Uncorr. ECC", ksegright) + "|\n";
|
|
452
|
+
out += "|" + fit(" Fan Temp Perf Pwr:Usage/Cap", ksegleft) +
|
|
453
|
+
"|" + fit(" Memory-Usage", ksegmid) + "|" +
|
|
454
|
+
fit(" GPU-Util Compute M.", ksegright) + "|\n";
|
|
455
|
+
out += segheavy + "\n";
|
|
456
|
+
|
|
457
|
+
for (unsigned i = 0; i < gpucount; ++i) {
|
|
458
|
+
const std::uint64_t digest = fnv1a(spec.name, i + 1);
|
|
459
|
+
const unsigned long long usedmib =
|
|
460
|
+
spec.totalmib / 10 +
|
|
461
|
+
digest % (spec.totalmib / 20); /* 10%..15% used at idle */
|
|
462
|
+
const unsigned draww = spec.powerw / 8 + digest % (spec.powerw / 4);
|
|
463
|
+
const std::string bus =
|
|
464
|
+
std::format("00000000:{:02X}:{:02X}.0", (7u + i * 4u) & 0xffu,
|
|
465
|
+
(i * 8u) & 0xffu);
|
|
466
|
+
/* datacenter boards (A100/H100/B200) have no readable fan sensor,
|
|
467
|
+
* nvidia-smi prints N/A; consumer/workstation boards report % */
|
|
468
|
+
const std::string fan =
|
|
469
|
+
spec.fanpct == 0 ? "N/A" : std::format("{}%", spec.fanpct);
|
|
470
|
+
const std::string powerstr =
|
|
471
|
+
std::format("{}W / {}W", draww, spec.powerw);
|
|
472
|
+
|
|
473
|
+
out += "|" +
|
|
474
|
+
fit(std::format(" {:>3} {:<24}{:>9} ", i, spec.name, "On"),
|
|
475
|
+
ksegleft) +
|
|
476
|
+
"|" + fit(std::format(" {} Off", bus), ksegmid) + "|" +
|
|
477
|
+
fitr("0", ksegright) + "|\n";
|
|
478
|
+
out += "|" +
|
|
479
|
+
fit(std::format(" {:>3} {:>4}C {:>3}{:>18}", fan, spec.tempc,
|
|
480
|
+
"P0", powerstr),
|
|
481
|
+
ksegleft) +
|
|
482
|
+
"|" +
|
|
483
|
+
fit(std::format(" {}MiB / {}MiB", usedmib, spec.totalmib),
|
|
484
|
+
ksegmid) +
|
|
485
|
+
"|" +
|
|
486
|
+
fit(std::format("{:>6}{:>15}",
|
|
487
|
+
std::format("{}%", spec.utilpct),
|
|
488
|
+
mig ? "E. Process" : "Default"),
|
|
489
|
+
ksegright) +
|
|
490
|
+
"|\n";
|
|
491
|
+
if (i + 1 < gpucount)
|
|
492
|
+
out += segborder + "\n";
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
out += segheavy + "\n";
|
|
496
|
+
out += "|" + fit(" Processes:", kinner) + "|\n";
|
|
497
|
+
if (mig && spec.mig) {
|
|
498
|
+
out += "|" +
|
|
499
|
+
fit(" GPU GI CI PID Type Process Name "
|
|
500
|
+
" GPU Memory",
|
|
501
|
+
kinner) +
|
|
502
|
+
"|\n";
|
|
503
|
+
for (unsigned g = 0; g < gpucount && g < 8; ++g)
|
|
504
|
+
out += "|" +
|
|
505
|
+
fit(std::format(" {:>3} {:>2} {:>2} {:>7} {:>4} "
|
|
506
|
+
"{:<20} {:>10}MiB",
|
|
507
|
+
g, 0, 0, 20000 + g * 37, "C+G",
|
|
508
|
+
"python", 2048 + g * 128),
|
|
509
|
+
kinner) +
|
|
510
|
+
"|\n";
|
|
511
|
+
} else {
|
|
512
|
+
out += "|" +
|
|
513
|
+
fit(std::format("{:^{}}", "No running processes found",
|
|
514
|
+
kinner),
|
|
515
|
+
kinner) +
|
|
516
|
+
"|\n";
|
|
517
|
+
}
|
|
518
|
+
out += fullborder + "\n";
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** default field list for csv mode, matching the nvidia-smi column names. */
|
|
523
|
+
inline constexpr std::string_view kqueryfields =
|
|
524
|
+
"index,name,uuid,driver_version,cuda_version,memory.total,memory.used,"
|
|
525
|
+
"memory.free,power.draw,power.limit,temperature.gpu,utilization.gpu";
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* renders nvidia-smi --query-gpu output in csv mode, the surface consumed
|
|
529
|
+
* by most fleet tooling (run-ai/fake-gpu-operator and cuda-mock implement
|
|
530
|
+
* the same subset). supported fields: index, name, uuid, driverversion,
|
|
531
|
+
* cudaversion, memory.total, memory.used, memory.free, power.draw,
|
|
532
|
+
* power.limit, temperature.gpu, utilization.gpu. unknown fields fail with
|
|
533
|
+
* spooferrc::unknownprofile exactly like the real binary refuses them.
|
|
534
|
+
* @param profile profile key
|
|
535
|
+
* @param gpucount device count
|
|
536
|
+
* @param fields comma-separated field list (may be empty for the default)
|
|
537
|
+
* @param noheader 1 suppresses the csv header line
|
|
538
|
+
* @param driver driver version
|
|
539
|
+
* @param cuda cuda version
|
|
540
|
+
* @return csv text or a spooferror
|
|
541
|
+
*/
|
|
542
|
+
[[nodiscard]] smiresult renderquerycsv(std::string_view profile,
|
|
543
|
+
unsigned gpucount,
|
|
544
|
+
std::string_view fields,
|
|
545
|
+
bool noheader,
|
|
546
|
+
std::string_view driver,
|
|
547
|
+
std::string_view cuda) {
|
|
548
|
+
if (gpucount == 0 || gpucount > 256)
|
|
549
|
+
return std::unexpected(makeerror(
|
|
550
|
+
spooferrc::invalidcount,
|
|
551
|
+
std::format("gpu count {} outside the supported [1, 256] range",
|
|
552
|
+
gpucount)));
|
|
553
|
+
auto specorerr = findspec(profile);
|
|
554
|
+
if (!specorerr)
|
|
555
|
+
return std::unexpected(specorerr.error());
|
|
556
|
+
const gpuspec& spec = **specorerr;
|
|
557
|
+
|
|
558
|
+
/* split the field list once; empty input selects every column */
|
|
559
|
+
std::vector<std::string> wanted;
|
|
560
|
+
{
|
|
561
|
+
std::string list{fields.empty() ? std::string_view{kqueryfields}
|
|
562
|
+
: fields};
|
|
563
|
+
std::size_t pos = 0;
|
|
564
|
+
while (pos != std::string::npos) {
|
|
565
|
+
std::size_t comma = list.find(',', pos);
|
|
566
|
+
std::string token =
|
|
567
|
+
list.substr(pos, comma == std::string::npos
|
|
568
|
+
? std::string::npos
|
|
569
|
+
: comma - pos);
|
|
570
|
+
/* nvidia-smi tolerates spaces after commas */
|
|
571
|
+
while (!token.empty() && token.front() == ' ')
|
|
572
|
+
token.erase(0, 1);
|
|
573
|
+
while (!token.empty() && token.back() == ' ')
|
|
574
|
+
token.pop_back();
|
|
575
|
+
if (!token.empty())
|
|
576
|
+
wanted.push_back(token);
|
|
577
|
+
pos = (comma == std::string::npos) ? std::string::npos
|
|
578
|
+
: comma + 1;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (wanted.empty())
|
|
582
|
+
return std::unexpected(makeerror(spooferrc::formatfailure,
|
|
583
|
+
"empty --query-gpu field list"));
|
|
584
|
+
|
|
585
|
+
/* validate every requested field before emitting anything */
|
|
586
|
+
for (const std::string& field : wanted) {
|
|
587
|
+
if (field != "index" && field != "name" && field != "uuid" &&
|
|
588
|
+
field != "driver_version" && field != "cuda_version" &&
|
|
589
|
+
field != "memory.total" && field != "memory.used" &&
|
|
590
|
+
field != "memory.free" && field != "power.draw" &&
|
|
591
|
+
field != "power.limit" && field != "temperature.gpu" &&
|
|
592
|
+
field != "utilization.gpu")
|
|
593
|
+
return std::unexpected(makeerror(
|
|
594
|
+
spooferrc::formatfailure,
|
|
595
|
+
std::format("field \"{}\" is not a valid field to query",
|
|
596
|
+
field)));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
std::string out;
|
|
600
|
+
if (!noheader) {
|
|
601
|
+
for (std::size_t f = 0; f < wanted.size(); ++f)
|
|
602
|
+
out += std::format("{}{}", f ? ", " : "", wanted[f]);
|
|
603
|
+
out += '\n';
|
|
604
|
+
}
|
|
605
|
+
for (unsigned i = 0; i < gpucount; ++i) {
|
|
606
|
+
const std::uint64_t digest = fnv1a(spec.name, i + 1);
|
|
607
|
+
const unsigned long long used =
|
|
608
|
+
spec.totalmib / 10 + digest % (spec.totalmib / 20);
|
|
609
|
+
for (std::size_t f = 0; f < wanted.size(); ++f) {
|
|
610
|
+
const std::string& field = wanted[f];
|
|
611
|
+
std::string value;
|
|
612
|
+
if (field == "index")
|
|
613
|
+
value = std::format("{}", i);
|
|
614
|
+
else if (field == "name")
|
|
615
|
+
value = std::string{spec.name};
|
|
616
|
+
else if (field == "uuid")
|
|
617
|
+
value = gpuuuid(digest);
|
|
618
|
+
else if (field == "driver_version")
|
|
619
|
+
value = std::string{driver};
|
|
620
|
+
else if (field == "cuda_version")
|
|
621
|
+
value = std::string{cuda};
|
|
622
|
+
else if (field == "memory.total")
|
|
623
|
+
value = std::format("{}", spec.totalmib);
|
|
624
|
+
else if (field == "memory.used")
|
|
625
|
+
value = std::format("{}", used);
|
|
626
|
+
else if (field == "memory.free")
|
|
627
|
+
value = std::format("{}", spec.totalmib - used);
|
|
628
|
+
else if (field == "power.draw")
|
|
629
|
+
value = std::format(
|
|
630
|
+
"{}", spec.powerw / 8 + digest % (spec.powerw / 4));
|
|
631
|
+
else if (field == "power.limit")
|
|
632
|
+
value = std::format("{}", spec.powerw);
|
|
633
|
+
else if (field == "temperature.gpu")
|
|
634
|
+
value = std::format("{}", spec.tempc);
|
|
635
|
+
else /* utilization.gpu */
|
|
636
|
+
value = std::format("{}", spec.utilpct);
|
|
637
|
+
out += std::format("{}{}", f ? ", " : "", value);
|
|
638
|
+
}
|
|
639
|
+
out += '\n';
|
|
640
|
+
}
|
|
641
|
+
return out;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* renders nvidia-smi -L device list lines:
|
|
646
|
+
* "GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-...)".
|
|
647
|
+
* @param profile profile key
|
|
648
|
+
* @param gpucount device count
|
|
649
|
+
* @return list text or a spooferror
|
|
650
|
+
*/
|
|
651
|
+
[[nodiscard]] smiresult renderdevicelist(std::string_view profile,
|
|
652
|
+
unsigned gpucount) {
|
|
653
|
+
auto specorerr = findspec(profile);
|
|
654
|
+
if (!specorerr)
|
|
655
|
+
return std::unexpected(specorerr.error());
|
|
656
|
+
const gpuspec& spec = **specorerr;
|
|
657
|
+
std::string out;
|
|
658
|
+
for (unsigned i = 0; i < gpucount; ++i)
|
|
659
|
+
out += std::format("GPU {}: {} (UUID: {})\n", i, spec.name,
|
|
660
|
+
gpuuuid(fnv1a(spec.name, i + 1)));
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
} /* namespace vhe */
|
|
665
|
+
|
|
666
|
+
/* -------------------------------------------------------------------- */
|
|
667
|
+
/* aetherforge control plane (v2 merge of forge.cpp/forge.hpp, mode 4) */
|
|
668
|
+
/* -------------------------------------------------------------------- */
|
|
669
|
+
|
|
670
|
+
namespace vhe::forge {
|
|
671
|
+
|
|
672
|
+
/** planner ceilings shared by validate() and the mttg grid; kvcpumax at
|
|
673
|
+
* 4096 matches the modern qemu -smp maxcpus limit (qemu 11.1.0 raised the
|
|
674
|
+
* x86 topology ceiling the engine relies on for 192-vcpu EPYC guests). */
|
|
675
|
+
inline constexpr std::uint32_t kvcpumax = 4096;
|
|
676
|
+
inline constexpr std::uint32_t kovercommitmax = 64;
|
|
677
|
+
inline constexpr std::uint32_t kmttgmax = 1'000'000;
|
|
678
|
+
|
|
679
|
+
/** planner error taxonomy; the values mirror the architect planner errors
|
|
680
|
+
* surfaced by index.ts so operators see one vocabulary across runtimes. */
|
|
681
|
+
enum class Err : std::uint8_t {
|
|
682
|
+
invalidplan, /**< empty plan name or unparsable input */
|
|
683
|
+
overcommit, /**< ratio above the 64x safety ceiling */
|
|
684
|
+
vendormismatch,/**< vfio plan without a matching host vendor */
|
|
685
|
+
missingvfio, /**< vfio plan on a host without vfio support */
|
|
686
|
+
mttgfloor, /**< mttg pool smaller than the vcpu count */
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
/** gpu attachment strategy for the emitted command line. */
|
|
690
|
+
enum class GpuMode : std::uint8_t { vfio, vgpu, mig, virtio, none };
|
|
691
|
+
|
|
692
|
+
/** hypervisor acceleration backend; whpx (windows hypervisor platform) is
|
|
693
|
+
* the only windows-capable backend in the engine and exists nowhere else
|
|
694
|
+
* in the pool outside this planner and its ts mirror. */
|
|
695
|
+
enum class Accel : std::uint8_t { kvm, hvf, whpx, tcg };
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* the complete lab plan, c++ mirror of the architect planner in index.ts.
|
|
699
|
+
* the defaults describe the reference 9950x3d + rtx 5090 lab node. the c++
|
|
700
|
+
* side owns the flags the ts planner delegates: hugepages file backend,
|
|
701
|
+
* virtio-mem dimm, swtpm tpm-crb device and the looking glass ivshmem.
|
|
702
|
+
*/
|
|
703
|
+
struct Plan {
|
|
704
|
+
std::string name{"aether-lab"};
|
|
705
|
+
std::string cpuid{"r9-9950x3d"};
|
|
706
|
+
std::string gpuid{"rtx-5090"};
|
|
707
|
+
std::uint32_t sockets{1};
|
|
708
|
+
std::uint32_t dies{2};
|
|
709
|
+
std::uint32_t cores{8};
|
|
710
|
+
std::uint32_t threadspercore{2};
|
|
711
|
+
std::uint32_t vcpus{16};
|
|
712
|
+
std::uint32_t memorygib{64};
|
|
713
|
+
std::uint32_t vramgib{32};
|
|
714
|
+
std::uint32_t overcommit{1};
|
|
715
|
+
std::uint32_t mttgthreads{4096};
|
|
716
|
+
bool mttg{true};
|
|
717
|
+
bool balloon{true};
|
|
718
|
+
bool virtiomem{true};
|
|
719
|
+
bool hugepages{true};
|
|
720
|
+
bool tpm{true};
|
|
721
|
+
bool lookingglass{true};
|
|
722
|
+
GpuMode gpu{GpuMode::vfio};
|
|
723
|
+
Accel accel{Accel::kvm};
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* computes the full topology (sockets x dies x cores x threads) the -smp
|
|
728
|
+
* line must be able to express through maxcpus.
|
|
729
|
+
* @param p the plan
|
|
730
|
+
* @return product of every topology dimension
|
|
731
|
+
*/
|
|
732
|
+
[[nodiscard]] constexpr std::uint32_t topology(const Plan& p) noexcept {
|
|
733
|
+
return p.sockets * p.dies * p.cores * p.threadspercore;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** maps an accel backend to its qemu -machine accel= spelling. */
|
|
737
|
+
[[nodiscard]] constexpr std::string_view accelname(Accel a) noexcept {
|
|
738
|
+
switch (a) {
|
|
739
|
+
case Accel::kvm: return "kvm";
|
|
740
|
+
case Accel::hvf: return "hvf";
|
|
741
|
+
case Accel::whpx: return "whpx";
|
|
742
|
+
case Accel::tcg: return "tcg";
|
|
743
|
+
}
|
|
744
|
+
return "kvm";
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* validates and normalizes a plan. clamps stay deliberately forgiving
|
|
749
|
+
* (vcpus to 4096, overcommit to 64, mttg pool to one million threads,
|
|
750
|
+
* threadspercore folded to 1 or 2) while structural violations (mttg
|
|
751
|
+
* pool below the vcpu count, empty name) fail with a typed error.
|
|
752
|
+
* @param p candidate plan, modified in place when accepted
|
|
753
|
+
* @return the normalized plan or an Err code
|
|
754
|
+
*/
|
|
755
|
+
[[nodiscard]] std::expected<Plan, Err> validate(Plan p) {
|
|
756
|
+
p.vcpus = std::clamp(p.vcpus, 1u, kvcpumax);
|
|
757
|
+
p.overcommit = std::clamp(p.overcommit, 1u, kovercommitmax);
|
|
758
|
+
p.threadspercore = p.threadspercore == 1 ? 1 : 2;
|
|
759
|
+
if (p.mttg && p.mttgthreads < p.vcpus)
|
|
760
|
+
return std::unexpected(Err::mttgfloor);
|
|
761
|
+
p.mttgthreads = std::min(p.mttgthreads, kmttgmax);
|
|
762
|
+
if (p.name.empty())
|
|
763
|
+
return std::unexpected(Err::invalidplan);
|
|
764
|
+
return p;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* emits the qemu 11.1.0 argv for the plan. this generator is the superset
|
|
769
|
+
* of the ts planner: hugepages memory-backend-file with prealloc, the
|
|
770
|
+
* virtio-mem-pci dimm (block granularity 2M, requested size memory/4), the
|
|
771
|
+
* swtpm chardev/tpmdev/tpm-crb triplet, vfio behind a dedicated pcie-root
|
|
772
|
+
* port rp1, venus-enabled virtio-gpu-gl under egl-headless and the looking
|
|
773
|
+
* glass ivshmem-plain device on a shared /dev/shm memdev.
|
|
774
|
+
* @param p validated plan
|
|
775
|
+
* @return argv tokens, suitable for exec or shell quoting
|
|
776
|
+
*/
|
|
777
|
+
[[nodiscard]] std::vector<std::string> qemuargv(const Plan& p) {
|
|
778
|
+
const auto topo = topology(p);
|
|
779
|
+
const auto maxcpus = std::max(p.vcpus, topo);
|
|
780
|
+
std::vector<std::string> a{
|
|
781
|
+
"qemu-system-x86_64",
|
|
782
|
+
"-nodefaults",
|
|
783
|
+
"-machine",
|
|
784
|
+
std::format("q35,accel={},kernel-irqchip=split,hpet=off",
|
|
785
|
+
accelname(p.accel)),
|
|
786
|
+
"-cpu",
|
|
787
|
+
"host,kvm=on,l3-cache=on,topoext=on,host-cache-info=on,+x2apic",
|
|
788
|
+
"-smp",
|
|
789
|
+
std::format("cpus={},sockets={},dies={},cores={},threads={},maxcpus={}",
|
|
790
|
+
std::min(p.vcpus, topo), p.sockets, p.dies, p.cores,
|
|
791
|
+
p.threadspercore, maxcpus),
|
|
792
|
+
"-m",
|
|
793
|
+
std::format("{}G,slots=4,maxmem={}G", p.memorygib,
|
|
794
|
+
std::max(p.memorygib * 2, p.memorygib + 32)),
|
|
795
|
+
"-name",
|
|
796
|
+
std::format("{},debug-threads=on", p.name),
|
|
797
|
+
"-nographic",
|
|
798
|
+
"-serial",
|
|
799
|
+
"mon:stdio",
|
|
800
|
+
"-device",
|
|
801
|
+
"virtio-balloon-pci,deflate-on-oom=on,free-page-reporting=on",
|
|
802
|
+
"-device",
|
|
803
|
+
"virtio-net-pci,netdev=n0,mq=on,vectors=10",
|
|
804
|
+
"-netdev",
|
|
805
|
+
"tap,id=n0,vhost=on,queues=4,script=no,downscript=no",
|
|
806
|
+
};
|
|
807
|
+
if (p.hugepages) {
|
|
808
|
+
a.insert(a.end(),
|
|
809
|
+
{"-object",
|
|
810
|
+
std::format("memory-backend-file,id=mem0,size={}G,"
|
|
811
|
+
"mem-path=/dev/hugepages,share=on,prealloc=on",
|
|
812
|
+
p.memorygib),
|
|
813
|
+
"-numa", "node,memdev=mem0"});
|
|
814
|
+
}
|
|
815
|
+
if (p.virtiomem) {
|
|
816
|
+
a.insert(a.end(),
|
|
817
|
+
{"-device",
|
|
818
|
+
std::format("virtio-mem-pci,id=vmem0,memdev=mem1,"
|
|
819
|
+
"requested-size={}G",
|
|
820
|
+
std::max(1u, p.memorygib / 4)),
|
|
821
|
+
"-object",
|
|
822
|
+
std::format("memory-backend-ram,id=mem1,size={}G",
|
|
823
|
+
std::max(1u, p.memorygib / 2))});
|
|
824
|
+
}
|
|
825
|
+
if (p.tpm) {
|
|
826
|
+
a.insert(a.end(), {
|
|
827
|
+
"-chardev", "socket,id=chrtpm,path=/run/swtpm/swtpm-sock",
|
|
828
|
+
"-tpmdev", "emulator,id=tpm0,chardev=chrtpm",
|
|
829
|
+
"-device", "tpm-crb,tpmdev=tpm0",
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
switch (p.gpu) {
|
|
833
|
+
case GpuMode::vfio:
|
|
834
|
+
a.insert(a.end(), {
|
|
835
|
+
"-device",
|
|
836
|
+
"pcie-root-port,id=rp1,bus=pcie.0,slot=1,chassis=1,multifunction=on",
|
|
837
|
+
"-device",
|
|
838
|
+
"vfio-pci,host=01:00.0,bus=rp1,addr=00.0,multifunction=on,x-vga=on",
|
|
839
|
+
"-vga", "none",
|
|
840
|
+
});
|
|
841
|
+
break;
|
|
842
|
+
case GpuMode::virtio:
|
|
843
|
+
a.insert(a.end(), {"-device", "virtio-gpu-gl,hostmem=8G,blob=on,venus=on",
|
|
844
|
+
"-display", "egl-headless,gl=on"});
|
|
845
|
+
break;
|
|
846
|
+
default:
|
|
847
|
+
a.insert(a.end(), {"-vga", "none"});
|
|
848
|
+
break;
|
|
849
|
+
}
|
|
850
|
+
if (p.lookingglass && p.gpu == GpuMode::vfio) {
|
|
851
|
+
a.insert(a.end(), {
|
|
852
|
+
"-device", "ivshmem-plain,memdev=ivshmem",
|
|
853
|
+
"-object",
|
|
854
|
+
"memory-backend-file,id=ivshmem,share=on,"
|
|
855
|
+
"mem-path=/dev/shm/looking-glass,size=128M",
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
if (p.mttg) {
|
|
859
|
+
a.insert(a.end(),
|
|
860
|
+
{"-fw_cfg",
|
|
861
|
+
std::format("name=opt/aetherforge/mttg,string={}",
|
|
862
|
+
p.mttgthreads)});
|
|
863
|
+
}
|
|
864
|
+
return a;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* renders the argv as a copy-pasteable shell command; tokens containing
|
|
869
|
+
* spaces or commas are single-quoted.
|
|
870
|
+
* @param p validated plan
|
|
871
|
+
* @return multi-line command text
|
|
872
|
+
*/
|
|
873
|
+
[[nodiscard]] std::string qemucommand(const Plan& p) {
|
|
874
|
+
std::ostringstream os;
|
|
875
|
+
bool first = true;
|
|
876
|
+
for (const auto& tok : qemuargv(p)) {
|
|
877
|
+
if (!first)
|
|
878
|
+
os << " \\\n ";
|
|
879
|
+
first = false;
|
|
880
|
+
if (tok.find(' ') != std::string::npos ||
|
|
881
|
+
tok.find(',') != std::string::npos) {
|
|
882
|
+
os << '\'' << tok << '\'';
|
|
883
|
+
} else {
|
|
884
|
+
os << tok;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return os.str();
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* mttg (multi-threaded tcg) grid model: host lanes multiplex a larger
|
|
892
|
+
* virtual thread pool by parking the surplus and rotating parked threads
|
|
893
|
+
* onto idle lanes (work stealing). the counters feed the multiplex ratio
|
|
894
|
+
* the engine prints next to every emitted plan.
|
|
895
|
+
*/
|
|
896
|
+
class Mttg {
|
|
897
|
+
public:
|
|
898
|
+
/**
|
|
899
|
+
* builds the grid from the host lane count and the requested virtual
|
|
900
|
+
* pool; the runnable set never exceeds the host lanes.
|
|
901
|
+
* @param hostthreads physical lanes (>= 1)
|
|
902
|
+
* @param virtualthreads requested pool size
|
|
903
|
+
*/
|
|
904
|
+
explicit Mttg(std::uint32_t hostthreads, std::uint32_t virtualthreads)
|
|
905
|
+
: mhost(std::max(1u, hostthreads)),
|
|
906
|
+
mvirtual(std::max(mhost, virtualthreads)),
|
|
907
|
+
mrunnable(std::min(mhost, mvirtual)) {
|
|
908
|
+
mparked = mvirtual - mrunnable;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/** grows the virtual pool by n threads, unparking as lanes free up. */
|
|
912
|
+
void spawn(std::uint32_t n) {
|
|
913
|
+
mvirtual = std::min(kmttgmax, mvirtual + n);
|
|
914
|
+
const auto want = std::min(mhost, mvirtual);
|
|
915
|
+
if (mrunnable < want) {
|
|
916
|
+
const auto take = want - mrunnable;
|
|
917
|
+
mparked -= std::min(mparked, take);
|
|
918
|
+
mrunnable += take;
|
|
919
|
+
} else {
|
|
920
|
+
mparked += n;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** work-stealing slice: parked threads rotate onto idle host lanes and
|
|
925
|
+
* return to the park set after their quantum, keeping occupancy
|
|
926
|
+
* host-bound exactly like mttg's round-robin vcpu scheduler. */
|
|
927
|
+
void steal() {
|
|
928
|
+
if (mparked == 0)
|
|
929
|
+
return;
|
|
930
|
+
const auto rotate = std::min(mparked, mhost);
|
|
931
|
+
mparked -= rotate;
|
|
932
|
+
mrunnable = std::min(mhost, mrunnable + rotate);
|
|
933
|
+
mparked += rotate; /* they go back after a slice */
|
|
934
|
+
mrunnable = mhost;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/** @return threads currently parked in the pool */
|
|
938
|
+
[[nodiscard]] std::uint32_t parked() const noexcept { return mparked; }
|
|
939
|
+
|
|
940
|
+
/** @return threads currently bound to a host lane */
|
|
941
|
+
[[nodiscard]] std::uint32_t runnable() const noexcept { return mrunnable; }
|
|
942
|
+
|
|
943
|
+
/** @return virtual pool divided by host lanes */
|
|
944
|
+
[[nodiscard]] double multiplex() const noexcept {
|
|
945
|
+
return static_cast<double>(mvirtual) /
|
|
946
|
+
static_cast<double>(mhost);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
private:
|
|
950
|
+
std::uint32_t mhost;
|
|
951
|
+
std::uint32_t mvirtual;
|
|
952
|
+
std::uint32_t mparked{0};
|
|
953
|
+
std::uint32_t mrunnable{0};
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
} /* namespace vhe::forge */
|
|
957
|
+
|
|
958
|
+
/* -------------------------------------------------------------------- */
|
|
959
|
+
/* build mode 2: nvml + cuda interposition shim (unified-shim pattern) */
|
|
960
|
+
/* -------------------------------------------------------------------- */
|
|
961
|
+
|
|
962
|
+
#if defined(VHE_BUILD_PRELOAD)
|
|
963
|
+
|
|
964
|
+
extern "C" {
|
|
965
|
+
|
|
966
|
+
/* nvml abi surface, trimmed to what pynvml, gpustat and dcgm probe first. */
|
|
967
|
+
typedef int nvmlReturn_t; /* 0 = NVML_SUCCESS */
|
|
968
|
+
typedef struct nvmlDevice_st* nvmlDevice_t; /* opaque handle */
|
|
969
|
+
typedef struct { unsigned long long total, used, free; } nvmlMemory_t;
|
|
970
|
+
typedef struct { unsigned gpu, memory, encoder, decoder; } nvmlUtilization_t;
|
|
971
|
+
|
|
972
|
+
#define NVML_SUCCESS 0
|
|
973
|
+
#define NVML_ERROR_NOT_FOUND -9
|
|
974
|
+
#define NVML_ERROR_INVALID -1
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* resolves the real nvml symbol after this object, when a physical driver
|
|
978
|
+
* exists in the process. the unified shim forwards first and fabricates
|
|
979
|
+
* only on absence, exactly like rick-hsu/nvml-unified-shim on DGX hosts.
|
|
980
|
+
* @param name nvml symbol name
|
|
981
|
+
* @return real function pointer or nullptr
|
|
982
|
+
*/
|
|
983
|
+
static void* realsymbol(const char* name) {
|
|
984
|
+
return dlsym(RTLD_NEXT, name);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/** returns the active profile key from the environment. */
|
|
988
|
+
static std::string shimprofile() {
|
|
989
|
+
return vhe::envstr("VHE_GPU_PROFILE").value_or("a100");
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/** returns the active device count from the environment. */
|
|
993
|
+
static unsigned shimcount() {
|
|
994
|
+
return vhe::envuint("VHE_GPUS", 8, 1, 256);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/** maps a fake handle back to its device index (index-as-pointer). */
|
|
998
|
+
static unsigned shimindex(nvmlDevice_t device) {
|
|
999
|
+
return device ? static_cast<unsigned>(
|
|
1000
|
+
reinterpret_cast<std::uintptr_t>(device) - 1)
|
|
1001
|
+
: 0;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
nvmlReturn_t nvmlInit_v2(void) {
|
|
1005
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(void)>(
|
|
1006
|
+
realsymbol("nvmlInit_v2"));
|
|
1007
|
+
return real ? real() : NVML_SUCCESS;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
nvmlReturn_t nvmlShutdown(void) {
|
|
1011
|
+
auto real =
|
|
1012
|
+
reinterpret_cast<nvmlReturn_t (*)(void)>(realsymbol("nvmlShutdown"));
|
|
1013
|
+
return real ? real() : NVML_SUCCESS;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
nvmlReturn_t nvmlSystemGetDriverVersion(char* version,
|
|
1017
|
+
unsigned int length) {
|
|
1018
|
+
if (version == nullptr || length == 0)
|
|
1019
|
+
return NVML_ERROR_INVALID;
|
|
1020
|
+
std::snprintf(version, length, "%s",
|
|
1021
|
+
vhe::envstr("VHE_SMI_DRIVER").value_or("575.57.08").c_str());
|
|
1022
|
+
return NVML_SUCCESS;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
nvmlReturn_t nvmlSystemGetCudaDriverVersion(int* cudaversion) {
|
|
1026
|
+
if (cudaversion == nullptr)
|
|
1027
|
+
return NVML_ERROR_INVALID;
|
|
1028
|
+
*cudaversion = 12090; /* 1000*major + 10*minor = CUDA 12.9 */
|
|
1029
|
+
return NVML_SUCCESS;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
nvmlReturn_t nvmlDeviceGetCount_v2(unsigned int* count) {
|
|
1033
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(unsigned int*)>(
|
|
1034
|
+
realsymbol("nvmlDeviceGetCount_v2"));
|
|
1035
|
+
if (real != nullptr)
|
|
1036
|
+
return real(count);
|
|
1037
|
+
if (count == nullptr)
|
|
1038
|
+
return NVML_ERROR_INVALID;
|
|
1039
|
+
*count = shimcount();
|
|
1040
|
+
return NVML_SUCCESS;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
nvmlReturn_t nvmlDeviceGetHandleByIndex_v2(unsigned int index,
|
|
1044
|
+
nvmlDevice_t* device) {
|
|
1045
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(unsigned int,
|
|
1046
|
+
nvmlDevice_t*)>(
|
|
1047
|
+
realsymbol("nvmlDeviceGetHandleByIndex_v2"));
|
|
1048
|
+
if (real != nullptr)
|
|
1049
|
+
return real(index, device);
|
|
1050
|
+
if (device == nullptr || index >= shimcount())
|
|
1051
|
+
return NVML_ERROR_INVALID;
|
|
1052
|
+
/* index-as-pointer fake handles, unified-shim style */
|
|
1053
|
+
*device = reinterpret_cast<nvmlDevice_t>(
|
|
1054
|
+
static_cast<std::uintptr_t>(index + 1));
|
|
1055
|
+
return NVML_SUCCESS;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
nvmlReturn_t nvmlDeviceGetName(nvmlDevice_t device, char* name,
|
|
1059
|
+
unsigned int length) {
|
|
1060
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t, char*,
|
|
1061
|
+
unsigned int)>(
|
|
1062
|
+
realsymbol("nvmlDeviceGetName"));
|
|
1063
|
+
if (real != nullptr)
|
|
1064
|
+
return real(device, name, length);
|
|
1065
|
+
if (name == nullptr || length == 0)
|
|
1066
|
+
return NVML_ERROR_INVALID;
|
|
1067
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1068
|
+
if (!spec)
|
|
1069
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1070
|
+
std::snprintf(name, length, "%s", (*spec)->name.data());
|
|
1071
|
+
return NVML_SUCCESS;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
nvmlReturn_t nvmlDeviceGetMemoryInfo(nvmlDevice_t device,
|
|
1075
|
+
nvmlMemory_t* memory) {
|
|
1076
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
|
|
1077
|
+
nvmlMemory_t*)>(
|
|
1078
|
+
realsymbol("nvmlDeviceGetMemoryInfo"));
|
|
1079
|
+
if (real != nullptr)
|
|
1080
|
+
return real(device, memory);
|
|
1081
|
+
if (memory == nullptr)
|
|
1082
|
+
return NVML_ERROR_INVALID;
|
|
1083
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1084
|
+
if (!spec)
|
|
1085
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1086
|
+
const unsigned long long total = (*spec)->totalmib << 20; /* MiB to B */
|
|
1087
|
+
const unsigned long long used =
|
|
1088
|
+
total / 10 +
|
|
1089
|
+
vhe::fnv1a((*spec)->name, shimindex(device) + 1) % (total / 20);
|
|
1090
|
+
memory->total = total;
|
|
1091
|
+
memory->used = used;
|
|
1092
|
+
memory->free = total - used;
|
|
1093
|
+
return NVML_SUCCESS;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
nvmlReturn_t nvmlDeviceGetPowerManagementLimit(nvmlDevice_t device,
|
|
1097
|
+
unsigned int* limit) {
|
|
1098
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
|
|
1099
|
+
unsigned int*)>(
|
|
1100
|
+
realsymbol("nvmlDeviceGetPowerManagementLimit"));
|
|
1101
|
+
if (real != nullptr)
|
|
1102
|
+
return real(device, limit);
|
|
1103
|
+
if (limit == nullptr)
|
|
1104
|
+
return NVML_ERROR_INVALID;
|
|
1105
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1106
|
+
if (!spec)
|
|
1107
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1108
|
+
*limit = (*spec)->powerw * 1000; /* nvml reports milliwatts */
|
|
1109
|
+
return NVML_SUCCESS;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
nvmlReturn_t nvmlDeviceGetTemperature(nvmlDevice_t device, int sensortype,
|
|
1113
|
+
unsigned int* temp) {
|
|
1114
|
+
(void)sensortype; /* only NVML_TEMPERATURE_GPU (0) is emulated */
|
|
1115
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t, int,
|
|
1116
|
+
unsigned int*)>(
|
|
1117
|
+
realsymbol("nvmlDeviceGetTemperature"));
|
|
1118
|
+
if (real != nullptr)
|
|
1119
|
+
return real(device, sensortype, temp);
|
|
1120
|
+
if (temp == nullptr)
|
|
1121
|
+
return NVML_ERROR_INVALID;
|
|
1122
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1123
|
+
if (!spec)
|
|
1124
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1125
|
+
*temp = (*spec)->tempc;
|
|
1126
|
+
return NVML_SUCCESS;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
nvmlReturn_t nvmlDeviceGetUtilizationRates(nvmlDevice_t device,
|
|
1130
|
+
nvmlUtilization_t* util) {
|
|
1131
|
+
auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
|
|
1132
|
+
nvmlUtilization_t*)>(
|
|
1133
|
+
realsymbol("nvmlDeviceGetUtilizationRates"));
|
|
1134
|
+
if (real != nullptr)
|
|
1135
|
+
return real(device, util);
|
|
1136
|
+
if (util == nullptr)
|
|
1137
|
+
return NVML_ERROR_INVALID;
|
|
1138
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1139
|
+
if (!spec)
|
|
1140
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1141
|
+
util->gpu = (*spec)->utilpct;
|
|
1142
|
+
util->memory = (*spec)->utilpct / 3;
|
|
1143
|
+
util->encoder = 0;
|
|
1144
|
+
util->decoder = 0;
|
|
1145
|
+
return NVML_SUCCESS;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
nvmlReturn_t nvmlErrorString(nvmlReturn_t result) {
|
|
1149
|
+
(void)result;
|
|
1150
|
+
return NVML_SUCCESS; /* simplified: callers rarely print shim errors */
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/* cuda driver api surface, the minimal entry-point set FanBB2333/GpuAdapter
|
|
1154
|
+
* intercepts before applications reach libcudart; types follow cuda.h. */
|
|
1155
|
+
typedef unsigned int CUresult; /* 0 = CUDA_SUCCESS */
|
|
1156
|
+
typedef int CUdevice; /* ordinal */
|
|
1157
|
+
|
|
1158
|
+
nvmlReturn_t cuInit(unsigned int flags) {
|
|
1159
|
+
auto real = reinterpret_cast<CUresult (*)(unsigned int)>(
|
|
1160
|
+
realsymbol("cuInit"));
|
|
1161
|
+
return real ? static_cast<nvmlReturn_t>(real(flags)) : NVML_SUCCESS;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
nvmlReturn_t cuDriverGetVersion(int* version) {
|
|
1165
|
+
auto real = reinterpret_cast<CUresult (*)(int*)>(
|
|
1166
|
+
realsymbol("cuDriverGetVersion"));
|
|
1167
|
+
if (real != nullptr)
|
|
1168
|
+
return static_cast<nvmlReturn_t>(real(version));
|
|
1169
|
+
if (version != nullptr)
|
|
1170
|
+
*version = 12090; /* CUDA 12.9 */
|
|
1171
|
+
return NVML_SUCCESS;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
nvmlReturn_t cuDeviceGetCount(int* count) {
|
|
1175
|
+
auto real = reinterpret_cast<CUresult (*)(int*)>(
|
|
1176
|
+
realsymbol("cuDeviceGetCount"));
|
|
1177
|
+
if (real != nullptr)
|
|
1178
|
+
return static_cast<nvmlReturn_t>(real(count));
|
|
1179
|
+
if (count != nullptr)
|
|
1180
|
+
*count = static_cast<int>(shimcount());
|
|
1181
|
+
return NVML_SUCCESS;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
nvmlReturn_t cuDeviceGet(CUdevice* device, int ordinal) {
|
|
1185
|
+
auto real = reinterpret_cast<CUresult (*)(CUdevice*, int)>(
|
|
1186
|
+
realsymbol("cuDeviceGet"));
|
|
1187
|
+
if (real != nullptr)
|
|
1188
|
+
return static_cast<nvmlReturn_t>(real(device, ordinal));
|
|
1189
|
+
if (device != nullptr && ordinal >= 0 &&
|
|
1190
|
+
static_cast<unsigned>(ordinal) < shimcount())
|
|
1191
|
+
*device = ordinal;
|
|
1192
|
+
return NVML_SUCCESS;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
nvmlReturn_t cuDeviceGetName(char* name, int len, CUdevice device) {
|
|
1196
|
+
auto real = reinterpret_cast<CUresult (*)(char*, int, CUdevice)>(
|
|
1197
|
+
realsymbol("cuDeviceGetName"));
|
|
1198
|
+
if (real != nullptr)
|
|
1199
|
+
return static_cast<nvmlReturn_t>(real(name, len, device));
|
|
1200
|
+
if (name == nullptr || len <= 0)
|
|
1201
|
+
return NVML_ERROR_INVALID;
|
|
1202
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1203
|
+
if (!spec)
|
|
1204
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1205
|
+
std::snprintf(name, static_cast<std::size_t>(len), "%s",
|
|
1206
|
+
(*spec)->name.data());
|
|
1207
|
+
return NVML_SUCCESS;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
nvmlReturn_t cuDeviceTotalMem_v2(std::size_t* bytes, CUdevice device) {
|
|
1211
|
+
auto real = reinterpret_cast<CUresult (*)(std::size_t*, CUdevice)>(
|
|
1212
|
+
realsymbol("cuDeviceTotalMem_v2"));
|
|
1213
|
+
if (real != nullptr)
|
|
1214
|
+
return static_cast<nvmlReturn_t>(real(bytes, device));
|
|
1215
|
+
if (bytes == nullptr)
|
|
1216
|
+
return NVML_ERROR_INVALID;
|
|
1217
|
+
auto spec = vhe::findspec(shimprofile());
|
|
1218
|
+
if (!spec)
|
|
1219
|
+
return NVML_ERROR_NOT_FOUND;
|
|
1220
|
+
*bytes = (*spec)->totalmib << 20;
|
|
1221
|
+
return NVML_SUCCESS;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
nvmlReturn_t cuGetErrorString(CUresult error, const char** str) {
|
|
1225
|
+
(void)error;
|
|
1226
|
+
if (str != nullptr)
|
|
1227
|
+
*str = "no error"; /* gpuadapter reports a benign string */
|
|
1228
|
+
return NVML_SUCCESS;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
} /* extern "C" */
|
|
1232
|
+
|
|
1233
|
+
#endif /* VHE_BUILD_PRELOAD */
|
|
1234
|
+
|
|
1235
|
+
/* -------------------------------------------------------------------- */
|
|
1236
|
+
/* build mode 3: node 26 native addon (node_api.h) */
|
|
1237
|
+
/* -------------------------------------------------------------------- */
|
|
1238
|
+
|
|
1239
|
+
#if defined(VHE_BUILD_ADDON)
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* napi wrapper around rendertable. signature:
|
|
1243
|
+
* generateSmi(profile?: string, gpuCount?: number, opts?: object)
|
|
1244
|
+
* opts fields: driver, cuda, mig, csv (booleans/strings).
|
|
1245
|
+
* errors surface as napi_throw_error with the spooferror detail, so
|
|
1246
|
+
* javascript callers handle failures through try/catch while the c++ side
|
|
1247
|
+
* stays exception-free.
|
|
1248
|
+
* @param env napi environment
|
|
1249
|
+
* @param info callback info with the arguments
|
|
1250
|
+
* @return string result or nullptr after throwing
|
|
1251
|
+
*/
|
|
1252
|
+
static napi_value generatesmi(napi_env env, napi_callback_info info) {
|
|
1253
|
+
std::size_t argc = 3;
|
|
1254
|
+
napi_value args[3];
|
|
1255
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr,
|
|
1256
|
+
nullptr);
|
|
1257
|
+
if (status != napi_ok) {
|
|
1258
|
+
napi_throw_error(env, nullptr, "napi: cannot read callback info");
|
|
1259
|
+
return nullptr;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
std::string profile = "a100";
|
|
1263
|
+
unsigned gpucount = 1;
|
|
1264
|
+
std::string driver{vhe::kdriverdefault};
|
|
1265
|
+
std::string cuda{vhe::kcudadefault};
|
|
1266
|
+
bool mig = false;
|
|
1267
|
+
bool csv = false;
|
|
1268
|
+
|
|
1269
|
+
if (argc >= 1 && args[0] != nullptr) {
|
|
1270
|
+
std::size_t len = 0;
|
|
1271
|
+
if (napi_get_value_string_utf8(env, args[0], nullptr, 0, &len) ==
|
|
1272
|
+
napi_ok &&
|
|
1273
|
+
len < 64) {
|
|
1274
|
+
profile.assign(len + 1, '\0');
|
|
1275
|
+
napi_get_value_string_utf8(env, args[0], profile.data(), len + 1,
|
|
1276
|
+
&len);
|
|
1277
|
+
profile.resize(len);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (argc >= 2 && args[1] != nullptr) {
|
|
1281
|
+
std::uint32_t n = 1;
|
|
1282
|
+
if (napi_get_value_uint32(env, args[1], &n) == napi_ok)
|
|
1283
|
+
gpucount = n;
|
|
1284
|
+
}
|
|
1285
|
+
if (argc >= 3 && args[2] != nullptr) {
|
|
1286
|
+
napi_value value;
|
|
1287
|
+
if (napi_get_named_property(env, args[2], "driver", &value) ==
|
|
1288
|
+
napi_ok &&
|
|
1289
|
+
value != nullptr) {
|
|
1290
|
+
std::size_t len = 0;
|
|
1291
|
+
if (napi_get_value_string_utf8(env, value, nullptr, 0, &len) ==
|
|
1292
|
+
napi_ok &&
|
|
1293
|
+
len < 32) {
|
|
1294
|
+
driver.assign(len + 1, '\0');
|
|
1295
|
+
napi_get_value_string_utf8(env, value, driver.data(), len + 1,
|
|
1296
|
+
&len);
|
|
1297
|
+
driver.resize(len);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
if (napi_get_named_property(env, args[2], "cuda", &value) == napi_ok &&
|
|
1301
|
+
value != nullptr) {
|
|
1302
|
+
std::size_t len = 0;
|
|
1303
|
+
if (napi_get_value_string_utf8(env, value, nullptr, 0, &len) ==
|
|
1304
|
+
napi_ok &&
|
|
1305
|
+
len < 32) {
|
|
1306
|
+
cuda.assign(len + 1, '\0');
|
|
1307
|
+
napi_get_value_string_utf8(env, value, cuda.data(), len + 1,
|
|
1308
|
+
&len);
|
|
1309
|
+
cuda.resize(len);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
bool flag = false;
|
|
1313
|
+
if (napi_get_named_property(env, args[2], "mig", &value) == napi_ok &&
|
|
1314
|
+
value != nullptr &&
|
|
1315
|
+
napi_get_value_bool(env, value, &flag) == napi_ok)
|
|
1316
|
+
mig = flag;
|
|
1317
|
+
if (napi_get_named_property(env, args[2], "csv", &value) == napi_ok &&
|
|
1318
|
+
value != nullptr &&
|
|
1319
|
+
napi_get_value_bool(env, value, &flag) == napi_ok)
|
|
1320
|
+
csv = flag;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
vhe::smiresult result =
|
|
1324
|
+
csv ? vhe::renderquerycsv(profile, gpucount, vhe::kqueryfields,
|
|
1325
|
+
false, driver, cuda)
|
|
1326
|
+
: vhe::rendertable(profile, gpucount, driver, cuda, mig);
|
|
1327
|
+
if (!result) {
|
|
1328
|
+
napi_throw_error(env, nullptr, result.error().detail.c_str());
|
|
1329
|
+
return nullptr;
|
|
1330
|
+
}
|
|
1331
|
+
napi_value out;
|
|
1332
|
+
status = napi_create_string_utf8(env, result->c_str(), result->size(),
|
|
1333
|
+
&out);
|
|
1334
|
+
if (status != napi_ok) {
|
|
1335
|
+
napi_throw_error(env, nullptr, "napi: cannot create result string");
|
|
1336
|
+
return nullptr;
|
|
1337
|
+
}
|
|
1338
|
+
return out;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/** module registration: exports generateSmi plus identity metadata. */
|
|
1342
|
+
static napi_value moduleinit(napi_env env, napi_value exports) {
|
|
1343
|
+
napi_value fn;
|
|
1344
|
+
if (napi_create_function(env, "generateSmi", NAPI_AUTO_LENGTH,
|
|
1345
|
+
generatesmi, nullptr, &fn) == napi_ok)
|
|
1346
|
+
napi_set_named_property(env, exports, "generateSmi", fn);
|
|
1347
|
+
|
|
1348
|
+
napi_value driver;
|
|
1349
|
+
if (napi_create_string_utf8(env, vhe::kdriverdefault.data(),
|
|
1350
|
+
vhe::kdriverdefault.size(),
|
|
1351
|
+
&driver) == napi_ok)
|
|
1352
|
+
napi_set_named_property(env, exports, "driverVersion", driver);
|
|
1353
|
+
|
|
1354
|
+
napi_value cuda;
|
|
1355
|
+
if (napi_create_string_utf8(env, vhe::kcudadefault.data(),
|
|
1356
|
+
vhe::kcudadefault.size(), &cuda) == napi_ok)
|
|
1357
|
+
napi_set_named_property(env, exports, "cudaVersion", cuda);
|
|
1358
|
+
|
|
1359
|
+
napi_value width;
|
|
1360
|
+
if (napi_create_uint32(env, static_cast<std::uint32_t>(vhe::ktable),
|
|
1361
|
+
&width) == napi_ok)
|
|
1362
|
+
napi_set_named_property(env, exports, "tableWidth", width);
|
|
1363
|
+
|
|
1364
|
+
return exports;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
NAPI_MODULE(NODE_GYP_MODULE_NAME, moduleinit)
|
|
1368
|
+
|
|
1369
|
+
#endif /* VHE_BUILD_ADDON */
|
|
1370
|
+
|
|
1371
|
+
/* -------------------------------------------------------------------- */
|
|
1372
|
+
/* build mode 1: the virtual nvidia-smi adapter command line */
|
|
1373
|
+
/* -------------------------------------------------------------------- */
|
|
1374
|
+
|
|
1375
|
+
#if defined(VHE_BUILD_CLI)
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* prints a short usage block; mirrors nvidia-smi --help spirit without
|
|
1379
|
+
* pretending to support the full flag surface.
|
|
1380
|
+
*/
|
|
1381
|
+
static void printusage() {
|
|
1382
|
+
std::println("nvidia-smi (virtual hardware engine v5 virtual identity)");
|
|
1383
|
+
std::println("usage: nvidia-smi [profile] [count] [options]");
|
|
1384
|
+
std::println(" options:");
|
|
1385
|
+
std::println(" -L, --list-gpus list devices with uuids");
|
|
1386
|
+
std::println(" --query-gpu=FIELDS -f csv csv query mode");
|
|
1387
|
+
std::println(" --format=csv,noheader csv without header line");
|
|
1388
|
+
std::println(" environment: VHE_GPU_PROFILE, VHE_GPUS, VHE_SMI_DRIVER,");
|
|
1389
|
+
std::println(" VHE_SMI_CUDA, VHE_MIG, VHE_DEBUG");
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* entry point: argument parsing with std::expected error propagation and
|
|
1394
|
+
* exit codes 0 (ok), 1 (render failure), 2 (usage failure).
|
|
1395
|
+
*/
|
|
1396
|
+
int main(int argc, char** argv) {
|
|
1397
|
+
std::string profile =
|
|
1398
|
+
vhe::envstr("VHE_GPU_PROFILE").value_or("a100");
|
|
1399
|
+
unsigned gpucount = vhe::envuint("VHE_GPUS", 8, 1, 256);
|
|
1400
|
+
std::string driver = vhe::envstr("VHE_SMI_DRIVER").value_or("575.57.08");
|
|
1401
|
+
std::string cuda = vhe::envstr("VHE_SMI_CUDA").value_or("12.9");
|
|
1402
|
+
std::string queryfields; /* filled from --query-gpu=... */
|
|
1403
|
+
bool listgpus = false;
|
|
1404
|
+
bool querycsv = false;
|
|
1405
|
+
bool noheader = false;
|
|
1406
|
+
bool mig = vhe::envuint("VHE_MIG", 0, 0, 1) != 0;
|
|
1407
|
+
|
|
1408
|
+
for (int i = 1; i < argc; ++i) {
|
|
1409
|
+
std::string_view arg{argv[i]};
|
|
1410
|
+
if (arg == "-L" || arg == "--list-gpus") {
|
|
1411
|
+
listgpus = true;
|
|
1412
|
+
} else if (arg.starts_with("--query-gpu=")) {
|
|
1413
|
+
querycsv = true;
|
|
1414
|
+
queryfields = std::string{
|
|
1415
|
+
arg.substr(std::string_view{"--query-gpu="}.size())};
|
|
1416
|
+
} else if (arg == "-f" || arg == "--format" ||
|
|
1417
|
+
arg.starts_with("--format=")) {
|
|
1418
|
+
std::string_view fmtspec =
|
|
1419
|
+
arg.starts_with("--format=")
|
|
1420
|
+
? arg.substr(std::string_view("--format=").size())
|
|
1421
|
+
: (i + 1 < argc ? std::string_view{argv[++i]}
|
|
1422
|
+
: std::string_view{"csv"});
|
|
1423
|
+
if (fmtspec.find("noheader") != std::string_view::npos)
|
|
1424
|
+
noheader = true;
|
|
1425
|
+
} else if (arg == "--help" || arg == "-h") {
|
|
1426
|
+
printusage();
|
|
1427
|
+
return 0;
|
|
1428
|
+
} else if (!arg.empty() && arg[0] != '-') {
|
|
1429
|
+
/* positional: first is profile, second is count */
|
|
1430
|
+
auto probe = vhe::findspec(arg);
|
|
1431
|
+
if (probe) {
|
|
1432
|
+
profile = std::string{arg};
|
|
1433
|
+
} else {
|
|
1434
|
+
try {
|
|
1435
|
+
unsigned n =
|
|
1436
|
+
static_cast<unsigned>(std::stoul(std::string{arg}));
|
|
1437
|
+
if (n >= 1 && n <= 256)
|
|
1438
|
+
gpucount = n;
|
|
1439
|
+
} catch (...) {
|
|
1440
|
+
std::println(stderr,
|
|
1441
|
+
"nvidia-smi: unrecognized argument '{}'",
|
|
1442
|
+
arg);
|
|
1443
|
+
printusage();
|
|
1444
|
+
return 2;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
} else {
|
|
1448
|
+
std::println(stderr, "nvidia-smi: unsupported flag '{}'", arg);
|
|
1449
|
+
printusage();
|
|
1450
|
+
return 2;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
vhe::smiresult result;
|
|
1455
|
+
if (listgpus) {
|
|
1456
|
+
result = vhe::renderdevicelist(profile, gpucount);
|
|
1457
|
+
} else if (querycsv) {
|
|
1458
|
+
result = vhe::renderquerycsv(profile, gpucount, queryfields,
|
|
1459
|
+
noheader, driver, cuda);
|
|
1460
|
+
} else {
|
|
1461
|
+
result = vhe::rendertable(profile, gpucount, driver, cuda, mig);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
if (!result) {
|
|
1465
|
+
std::println(stderr, "nvidia-smi: {}", result.error().detail);
|
|
1466
|
+
return 1;
|
|
1467
|
+
}
|
|
1468
|
+
std::print("{}", *result);
|
|
1469
|
+
return 0;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
#endif /* VHE_BUILD_CLI */
|
|
1473
|
+
|
|
1474
|
+
/* -------------------------------------------------------------------- */
|
|
1475
|
+
/* build mode 4: the aetherforge plan compiler */
|
|
1476
|
+
/* -------------------------------------------------------------------- */
|
|
1477
|
+
|
|
1478
|
+
#if defined(VHE_BUILD_FORGE)
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* prints a short usage block for the forge mode; the three flags cover the
|
|
1482
|
+
* dimensions operators tune per lab (vcpus, mttg pool, guest memory).
|
|
1483
|
+
*/
|
|
1484
|
+
static void printforgeusage() {
|
|
1485
|
+
std::println("aetherforge — emit a qemu 11.1.0 plan");
|
|
1486
|
+
std::println("usage: aetherforge [--smp N] [--mttg N] [--mem GIB]");
|
|
1487
|
+
std::println(" --smp N vcpus presented to the guest");
|
|
1488
|
+
std::println(" --mttg N virtual threads in the mttg pool");
|
|
1489
|
+
std::println(" --mem N guest memory in GiB");
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
/**
|
|
1493
|
+
* forge entry point: parses the three tuning flags, validates the plan
|
|
1494
|
+
* through the same expected<Plan, Err> channel the library exposes, prints
|
|
1495
|
+
* the mttg multiplex summary line and the fully quoted qemu command.
|
|
1496
|
+
* malformed numbers are caught and reported instead of aborting.
|
|
1497
|
+
*/
|
|
1498
|
+
int main(int argc, char** argv) {
|
|
1499
|
+
vhe::forge::Plan plan{};
|
|
1500
|
+
if (argc > 1 && std::string_view{argv[1]} == "--help") {
|
|
1501
|
+
printforgeusage();
|
|
1502
|
+
return 0;
|
|
1503
|
+
}
|
|
1504
|
+
for (int i = 1; i + 1 < argc; ++i) {
|
|
1505
|
+
const std::string_view k{argv[i]};
|
|
1506
|
+
const std::string_view v{argv[i + 1]};
|
|
1507
|
+
if (k != "--smp" && k != "--mttg" && k != "--mem")
|
|
1508
|
+
continue;
|
|
1509
|
+
unsigned parsed = 0;
|
|
1510
|
+
try {
|
|
1511
|
+
parsed = static_cast<unsigned>(std::stoul(std::string{v}));
|
|
1512
|
+
} catch (...) {
|
|
1513
|
+
std::println(stderr, "aetherforge: unparsable value '{}' for {}",
|
|
1514
|
+
v, k);
|
|
1515
|
+
return 2;
|
|
1516
|
+
}
|
|
1517
|
+
if (k == "--smp") {
|
|
1518
|
+
plan.vcpus = parsed;
|
|
1519
|
+
} else if (k == "--mttg") {
|
|
1520
|
+
plan.mttg = true;
|
|
1521
|
+
plan.mttgthreads = parsed;
|
|
1522
|
+
} else {
|
|
1523
|
+
plan.memorygib = parsed;
|
|
1524
|
+
}
|
|
1525
|
+
++i; /* the value token is consumed */
|
|
1526
|
+
}
|
|
1527
|
+
auto ok = vhe::forge::validate(std::move(plan));
|
|
1528
|
+
if (!ok) {
|
|
1529
|
+
std::println(stderr, "aetherforge: invalid plan (error {})",
|
|
1530
|
+
static_cast<unsigned>(ok.error()));
|
|
1531
|
+
return 1;
|
|
1532
|
+
}
|
|
1533
|
+
const auto host = std::max(
|
|
1534
|
+
1u, static_cast<unsigned>(std::thread::hardware_concurrency()));
|
|
1535
|
+
vhe::forge::Mttg grid{host, ok->mttgthreads};
|
|
1536
|
+
grid.steal();
|
|
1537
|
+
std::println("# host threads {} virtual {} multiplex {:.1f}x", host,
|
|
1538
|
+
ok->mttgthreads, grid.multiplex());
|
|
1539
|
+
std::println("{}\n", vhe::forge::qemucommand(*ok));
|
|
1540
|
+
return 0;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
#endif /* VHE_BUILD_FORGE */
|