windows-development-sandbox 1.9.0 → 1.11.3
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/README.md +64 -17
- package/config.example.json +7 -2
- package/dist/app/features/accounts/accounts.def.d.ts +122 -0
- package/dist/app/features/accounts/accounts.def.js +243 -0
- package/dist/app/features/accounts/accounts.run.d.ts +51 -0
- package/dist/app/features/accounts/accounts.run.js +125 -0
- package/dist/app/features/accounts/index.d.ts +2 -0
- package/dist/app/features/accounts/index.js +3 -0
- package/dist/app/features/agents/agent-entry.d.ts +17 -0
- package/dist/app/features/agents/agent-entry.js +50 -0
- package/dist/app/features/agents/agents.def.d.ts +65 -0
- package/dist/app/features/agents/agents.def.js +142 -0
- package/dist/app/features/agents/agents.run.d.ts +31 -0
- package/dist/app/features/agents/agents.run.js +189 -0
- package/dist/app/features/agents/index.d.ts +3 -0
- package/dist/app/features/agents/index.js +4 -0
- package/dist/app/features/apps/apps-rows.d.ts +31 -0
- package/dist/app/features/apps/apps-rows.js +73 -0
- package/dist/app/features/apps/apps.def.d.ts +181 -0
- package/dist/app/features/apps/apps.def.js +264 -0
- package/dist/app/features/apps/apps.run.d.ts +58 -0
- package/dist/app/features/apps/apps.run.js +76 -0
- package/dist/app/features/apps/index.d.ts +3 -0
- package/dist/app/features/apps/index.js +4 -0
- package/dist/app/features/data/data.def.d.ts +154 -0
- package/dist/app/features/data/data.def.js +201 -0
- package/dist/app/features/data/data.run.d.ts +46 -0
- package/dist/app/features/data/data.run.js +141 -0
- package/dist/app/features/data/index.d.ts +2 -0
- package/dist/app/features/data/index.js +3 -0
- package/dist/app/features/defs.d.ts +952 -4
- package/dist/app/features/defs.js +147 -10
- package/dist/app/features/engine/engine.def.d.ts +228 -0
- package/dist/app/features/engine/engine.def.js +194 -0
- package/dist/app/features/engine/engine.run.d.ts +72 -0
- package/dist/app/features/engine/engine.run.js +77 -0
- package/dist/app/features/engine/index.d.ts +2 -0
- package/dist/app/features/engine/index.js +4 -0
- package/dist/app/features/files/files.def.d.ts +51 -0
- package/dist/app/features/files/files.def.js +38 -0
- package/dist/app/features/files/files.run.d.ts +20 -0
- package/dist/app/features/files/files.run.js +59 -0
- package/dist/app/features/files/index.d.ts +2 -0
- package/dist/app/features/files/index.js +3 -0
- package/dist/app/features/github/github.def.d.ts +80 -0
- package/dist/app/features/github/github.def.js +142 -0
- package/dist/app/features/github/github.run.d.ts +36 -0
- package/dist/app/features/github/github.run.js +59 -0
- package/dist/app/features/github/index.d.ts +2 -0
- package/dist/app/features/github/index.js +3 -0
- package/dist/app/features/health/health.def.d.ts +275 -0
- package/dist/app/features/health/health.def.js +281 -0
- package/dist/app/features/health/health.run.d.ts +125 -0
- package/dist/app/features/health/health.run.js +204 -0
- package/dist/app/features/health/index.d.ts +2 -0
- package/dist/app/features/health/index.js +3 -0
- package/dist/app/features/logs/index.d.ts +2 -0
- package/dist/app/features/logs/index.js +3 -0
- package/dist/app/features/logs/logs.def.d.ts +78 -0
- package/dist/app/features/logs/logs.def.js +115 -0
- package/dist/app/features/logs/logs.run.d.ts +20 -0
- package/dist/app/features/logs/logs.run.js +42 -0
- package/dist/app/features/projects/index.d.ts +2 -0
- package/dist/app/features/projects/index.js +3 -0
- package/dist/app/features/projects/projects.def.d.ts +101 -0
- package/dist/app/features/projects/projects.def.js +208 -0
- package/dist/app/features/projects/projects.run.d.ts +56 -0
- package/dist/app/features/projects/projects.run.js +155 -0
- package/dist/app/features/sandbox/sandbox.def.js +11 -5
- package/dist/app/features/sessions/sessions.def.js +3 -0
- package/dist/app/features/settings/categories.def.js +3 -2
- package/dist/{cli/menu/settings/share.d.ts → app/features/settings/settings-share.d.ts} +1 -1
- package/dist/{cli/menu/settings/share.js → app/features/settings/settings-share.js} +3 -2
- package/dist/app/features/settings/settings.def.d.ts +22 -5
- package/dist/app/features/settings/settings.def.js +81 -6
- package/dist/app/features/settings/settings.run.d.ts +17 -0
- package/dist/app/features/settings/settings.run.js +89 -4
- package/dist/app/features/storage/index.d.ts +2 -0
- package/dist/app/features/storage/index.js +3 -0
- package/dist/app/features/storage/storage.def.d.ts +133 -0
- package/dist/app/features/storage/storage.def.js +225 -0
- package/dist/app/features/storage/storage.run.d.ts +62 -0
- package/dist/app/features/storage/storage.run.js +370 -0
- package/dist/app/features/updates/index.d.ts +3 -0
- package/dist/app/features/updates/index.js +4 -0
- package/dist/app/features/updates/update-lines.d.ts +13 -0
- package/dist/app/features/updates/update-lines.js +61 -0
- package/dist/app/features/updates/updates.def.d.ts +89 -0
- package/dist/app/features/updates/updates.def.js +225 -0
- package/dist/app/features/updates/updates.run.d.ts +48 -0
- package/dist/app/features/updates/updates.run.js +154 -0
- package/dist/app/features/vpn/index.d.ts +2 -0
- package/dist/app/features/vpn/index.js +3 -0
- package/dist/app/features/vpn/vpn.def.d.ts +112 -0
- package/dist/app/features/vpn/vpn.def.js +174 -0
- package/dist/app/features/vpn/vpn.run.d.ts +37 -0
- package/dist/app/features/vpn/vpn.run.js +54 -0
- package/dist/app/features/web/index.d.ts +2 -0
- package/dist/app/features/web/index.js +3 -0
- package/dist/app/features/web/web.def.d.ts +33 -0
- package/dist/app/features/web/web.def.js +90 -0
- package/dist/app/features/web/web.run.d.ts +24 -0
- package/dist/app/features/web/web.run.js +93 -0
- package/dist/app/kernel/define.d.ts +10 -1
- package/dist/app/kernel/io.d.ts +34 -0
- package/dist/app/kernel/io.js +9 -0
- package/dist/app/kernel/rpc.d.ts +17 -0
- package/dist/app/kernel/text.d.ts +2 -0
- package/dist/app/kernel/text.js +7 -0
- package/dist/app/kernel/view.d.ts +24 -1
- package/dist/app/kernel/view.js +1 -1
- package/dist/app/registry.js +74 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/commands/context.d.ts +7 -0
- package/dist/cli/commands/context.js +29 -2
- package/dist/cli/commands/engine.js +5 -58
- package/dist/cli/commands/github.d.ts +2 -0
- package/dist/cli/commands/github.js +11 -0
- package/dist/cli/commands/index.d.ts +3 -5
- package/dist/cli/commands/index.js +3 -5
- package/dist/cli/commands/launch/launch.js +23 -4
- package/dist/cli/commands/launch/running.js +4 -1
- package/dist/cli/commands/maintenance/config.js +1 -6
- package/dist/cli/commands/maintenance/index.d.ts +0 -1
- package/dist/cli/commands/maintenance/index.js +1 -2
- package/dist/cli/commands/maintenance/logs.js +27 -32
- package/dist/cli/commands/maintenance/migrate.d.ts +0 -8
- package/dist/cli/commands/maintenance/migrate.js +2 -44
- package/dist/cli/commands/maintenance/update.d.ts +0 -19
- package/dist/cli/commands/maintenance/update.js +1 -99
- package/dist/cli/commands/projects/index.d.ts +0 -1
- package/dist/cli/commands/projects/index.js +0 -1
- package/dist/cli/commands/registry.d.ts +1 -1
- package/dist/cli/commands/registry.js +29 -11
- package/dist/cli/commands/repair/checks/data.js +1 -1
- package/dist/cli/commands/repair/checks/hcs.js +28 -1
- package/dist/cli/commands/repair/checks/sandbox.js +1 -1
- package/dist/cli/commands/repair/health-run.d.ts +30 -0
- package/dist/cli/commands/repair/health-run.js +32 -0
- package/dist/cli/commands/repair/printer.d.ts +2 -2
- package/dist/cli/commands/repair/printer.js +2 -11
- package/dist/cli/commands/repair/run.d.ts +1 -21
- package/dist/cli/commands/repair/run.js +13 -70
- package/dist/cli/commands/storage/index.d.ts +0 -1
- package/dist/cli/commands/storage/index.js +0 -1
- package/dist/cli/commands/storage/offer.js +6 -2
- package/dist/cli/commands/vpn.d.ts +1 -3
- package/dist/cli/commands/vpn.js +8 -88
- package/dist/cli/commands/web.d.ts +7 -1
- package/dist/cli/commands/web.js +45 -71
- package/dist/cli/index.js +3 -2
- package/dist/cli/manifest.d.ts +16 -153
- package/dist/cli/manifest.js +20 -50
- package/dist/cli/menu/command-form.js +1 -2
- package/dist/cli/menu/io-menu.d.ts +2 -2
- package/dist/cli/menu/io-menu.js +6 -4
- package/dist/cli/menu/menu.js +1 -1
- package/dist/cli/menu/pages/accounts-page.d.ts +5 -17
- package/dist/cli/menu/pages/accounts-page.js +28 -170
- package/dist/cli/menu/pages/agents-page.d.ts +17 -18
- package/dist/cli/menu/pages/agents-page.js +67 -124
- package/dist/cli/menu/pages/apps-page.d.ts +15 -8
- package/dist/cli/menu/pages/apps-page.js +121 -29
- package/dist/cli/menu/pages/list-page.js +10 -1
- package/dist/cli/menu/pages/maintenance-page.d.ts +51 -52
- package/dist/cli/menu/pages/maintenance-page.js +246 -427
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -5
- package/dist/cli/menu/pages/sandbox-page.js +34 -112
- package/dist/cli/menu/pages/settings-page.d.ts +21 -3
- package/dist/cli/menu/pages/settings-page.js +89 -17
- package/dist/cli/menu/render-view.js +2 -1
- package/dist/cli/menu/settings/index.d.ts +0 -1
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/views.js +28 -0
- package/dist/cli/operations/handler.js +7 -3
- package/dist/cli/operations/input.d.ts +1 -1
- package/dist/cli/operations/input.js +29 -5
- package/dist/cli/operations/io-cli.js +9 -1
- package/dist/cli/operations/spec.js +8 -2
- package/dist/cli/output/render-view.js +2 -0
- package/dist/cli/{commands/data/collect.js → services/data-collect.js} +2 -2
- package/dist/cli/{commands/data/remove.js → services/data-remove.js} +2 -2
- package/dist/cli/services/data-service.d.ts +1 -1
- package/dist/cli/services/data-service.js +2 -2
- package/dist/cli/services/databases-service.d.ts +5 -0
- package/dist/cli/services/databases-service.js +15 -0
- package/dist/cli/services/health-service.d.ts +8 -4
- package/dist/cli/services/health-service.js +10 -8
- package/dist/cli/services/index.d.ts +4 -1
- package/dist/cli/services/index.js +4 -1
- package/dist/cli/services/logs-service.d.ts +7 -0
- package/dist/cli/services/logs-service.js +13 -0
- package/dist/cli/services/storage-service.d.ts +19 -0
- package/dist/cli/services/storage-service.js +71 -0
- package/dist/cli/services/vpn-service.d.ts +6 -4
- package/dist/cli/services/vpn-service.js +5 -11
- package/dist/cli/services/web-service.d.ts +4 -3
- package/dist/cli/services/web-service.js +12 -32
- package/dist/core/config/defaults.js +3 -2
- package/dist/core/schema/config.d.ts +6 -0
- package/dist/core/schema/config.js +2 -0
- package/dist/core/schema/github.d.ts +4 -0
- package/dist/core/schema/github.js +7 -0
- package/dist/core/schema/index.d.ts +1 -0
- package/dist/core/schema/index.js +1 -0
- package/dist/core/schema/profile.d.ts +1 -0
- package/dist/core/schema/profile.js +3 -0
- package/dist/core/schema/sandbox.js +3 -3
- package/dist/core/schema/toolchains.js +2 -2
- package/dist/core/schema/vpn.js +1 -1
- package/dist/core/schema/web.d.ts +1 -0
- package/dist/core/schema/web.js +4 -1
- package/dist/core/types.d.ts +6 -3
- package/dist/guest/boot/boot.js +11 -1
- package/dist/guest/daemon/daemon.js +1 -1
- package/dist/guest/daemon/sessions.d.ts +3 -2
- package/dist/guest/daemon/sessions.js +38 -7
- package/dist/guest/governor/core.d.ts +44 -0
- package/dist/guest/governor/core.js +124 -0
- package/dist/guest/governor/decide.d.ts +53 -15
- package/dist/guest/governor/decide.js +155 -72
- package/dist/guest/governor/governor.d.ts +2 -35
- package/dist/guest/governor/governor.js +35 -99
- package/dist/guest/governor/index.d.ts +1 -0
- package/dist/guest/governor/index.js +1 -0
- package/dist/guest/network/firewall.d.ts +3 -0
- package/dist/guest/network/firewall.js +65 -23
- package/dist/guest/runtime/process.js +53 -5
- package/dist/guest/sessions/viewer.js +5 -1
- package/dist/guest/setup/claude-mem.js +12 -4
- package/dist/guest/setup/github-token.d.ts +7 -0
- package/dist/guest/setup/github-token.js +65 -0
- package/dist/guest/toolchains/languages/node.js +40 -4
- package/dist/guest/toolchains/native/rust-extras.js +2 -2
- package/dist/guest/toolchains/tools/cli.d.ts +8 -0
- package/dist/guest/toolchains/tools/cli.js +16 -2
- package/dist/guest/vpn/openvpn.d.ts +5 -0
- package/dist/guest/vpn/openvpn.js +14 -1
- package/dist/guest/vpn/vpn.js +55 -16
- package/dist/guest/vpn/wireguard.js +2 -0
- package/dist/host/channel/attach.d.ts +1 -0
- package/dist/host/channel/attach.js +1 -0
- package/dist/host/channel/channel.d.ts +1 -1
- package/dist/host/channel/channel.js +2 -2
- package/dist/host/engines/hcs/check.d.ts +1 -0
- package/dist/host/engines/hcs/check.js +1 -1
- package/dist/host/engines/hcs/engine.d.ts +1 -1
- package/dist/host/engines/hcs/engine.js +6 -4
- package/dist/host/engines/hcs/helper.d.ts +11 -0
- package/dist/host/engines/hcs/helper.js +31 -2
- package/dist/host/engines/hcs/leftovers.d.ts +42 -0
- package/dist/host/engines/hcs/leftovers.js +101 -0
- package/dist/host/engines/index.d.ts +1 -0
- package/dist/host/engines/index.js +1 -0
- package/dist/host/github/deliver.d.ts +13 -0
- package/dist/host/github/deliver.js +51 -0
- package/dist/host/github/index.d.ts +2 -0
- package/dist/host/github/index.js +3 -0
- package/dist/host/github/token.d.ts +21 -0
- package/dist/host/github/token.js +74 -0
- package/dist/host/launch/generate.js +11 -5
- package/dist/host/launch/index.d.ts +1 -1
- package/dist/host/launch/index.js +1 -1
- package/dist/host/launch/session-file.js +1 -2
- package/dist/host/launch/wsb-file.d.ts +11 -7
- package/dist/host/launch/wsb-file.js +30 -12
- package/dist/host/life/host-memory.d.ts +1 -0
- package/dist/host/life/host-memory.js +4 -1
- package/dist/host/life/watchdog.d.ts +11 -0
- package/dist/host/life/watchdog.js +36 -4
- package/dist/host/sandbox/tasklist.d.ts +6 -0
- package/dist/host/sandbox/tasklist.js +37 -0
- package/dist/host/secrets/launch-secrets.d.ts +2 -1
- package/dist/host/secrets/launch-secrets.js +4 -2
- package/dist/host/vpn/status.js +13 -3
- package/dist/host/web/backend.d.ts +1 -3
- package/dist/host/web/index.d.ts +2 -1
- package/dist/host/web/index.js +2 -1
- package/dist/host/web/live.d.ts +23 -0
- package/dist/host/web/live.js +107 -0
- package/dist/host/web/rpc.d.ts +6 -1
- package/dist/host/web/rpc.js +23 -31
- package/dist/host/web/runner.d.ts +4 -0
- package/dist/host/web/runner.js +12 -1
- package/dist/host/web/server.d.ts +30 -2
- package/dist/host/web/server.js +87 -29
- package/dist/host/web/state.d.ts +1 -0
- package/dist/host/web/state.js +2 -0
- package/dist/runtime/agent.js +44 -16
- package/dist/runtime/daemon.js +1154 -857
- package/dist/runtime/db.js +44 -16
- package/dist/runtime/main.js +532 -115
- package/dist/runtime/netproxy.js +34 -9
- package/dist/runtime/view.js +499 -149
- package/dist/selftest/app.test.js +620 -12
- package/dist/selftest/channel.test.js +18 -1
- package/dist/selftest/cli.test.js +1 -1
- package/dist/selftest/config.test.js +1 -1
- package/dist/selftest/contained.test.js +1 -1
- package/dist/selftest/data.test.js +1 -2
- package/dist/selftest/engine.test.js +3 -1
- package/dist/selftest/github.test.d.ts +2 -0
- package/dist/selftest/github.test.js +243 -0
- package/dist/selftest/guest-boot.test.js +47 -1
- package/dist/selftest/hcs.test.js +105 -8
- package/dist/selftest/health.test.js +71 -0
- package/dist/selftest/heavy-load.test.js +152 -66
- package/dist/selftest/index.js +4 -0
- package/dist/selftest/layout.test.js +8 -0
- package/dist/selftest/live-terminal.test.js +74 -0
- package/dist/selftest/menu-screens.js +171 -56
- package/dist/selftest/menu.test.js +54 -37
- package/dist/selftest/migrate.test.js +14 -12
- package/dist/selftest/network.test.js +20 -1
- package/dist/selftest/powershell-variables.test.d.ts +2 -0
- package/dist/selftest/powershell-variables.test.js +61 -0
- package/dist/selftest/support/engine-golden.js +1 -0
- package/dist/selftest/support/fixtures.d.ts +5 -0
- package/dist/selftest/support/web-preview.d.ts +1 -0
- package/dist/selftest/support/web-preview.js +856 -0
- package/dist/selftest/update-service.test.js +15 -8
- package/dist/selftest/vpn.test.js +86 -2
- package/dist/selftest/web.test.js +58 -11
- package/dist/shared/channel/run-dir.d.ts +2 -0
- package/dist/shared/channel/run-dir.js +3 -0
- package/dist/shared/channel/schema.d.ts +7 -0
- package/dist/shared/channel/schema.js +9 -1
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +3 -0
- package/dist/shared/github/index.d.ts +1 -0
- package/dist/shared/github/index.js +2 -0
- package/dist/shared/github/token.d.ts +29 -0
- package/dist/shared/github/token.js +89 -0
- package/dist/shared/io/copy-tree.d.ts +7 -0
- package/dist/shared/io/copy-tree.js +45 -0
- package/dist/shared/io/index.d.ts +2 -0
- package/dist/shared/io/index.js +2 -0
- package/dist/shared/io/robocopy-result.d.ts +6 -0
- package/dist/shared/io/robocopy-result.js +23 -0
- package/dist/shared/memory/policy.d.ts +38 -12
- package/dist/shared/memory/policy.js +58 -19
- package/dist/shared/network/firewall-rules.d.ts +8 -0
- package/dist/shared/network/firewall-rules.js +28 -0
- package/dist/shared/pty/ctrl-c.d.ts +18 -0
- package/dist/shared/pty/ctrl-c.js +36 -0
- package/dist/shared/pty/index.d.ts +1 -0
- package/dist/shared/pty/index.js +1 -0
- package/dist/shared/pty/terminal.d.ts +2 -0
- package/dist/shared/pty/terminal.js +21 -2
- package/dist/shared/vpn/kill-switch.d.ts +13 -2
- package/dist/shared/vpn/kill-switch.js +88 -8
- package/dist/shared/web/protocol.d.ts +0 -4
- package/dist/sync/watcher.js +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/{main-T5DMCHYV.css → main-6JF3ASB5.css} +1 -1
- package/dist/web/main-Z645W45E.js +219 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/MemoryStatus.cs +128 -107
- package/native/scripts/memory-status.ps1 +10 -2
- package/package.json +10 -3
- package/sandbox/csharp/MemGov.cs +668 -398
- package/sandbox/csharp/PtyHost.cs +104 -15
- package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
- package/sandbox/csharp/SandboxTools/Program.cs +1 -1
- package/sandbox/scripts/new-shortcuts.ps1 +3 -1
- package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
- package/schemas/config.schema.json +25 -7
- package/schemas/sandbox-config.schema.json +7 -3
- package/dist/cli/commands/accounts.d.ts +0 -2
- package/dist/cli/commands/accounts.js +0 -96
- package/dist/cli/commands/data/clean.d.ts +0 -3
- package/dist/cli/commands/data/clean.js +0 -60
- package/dist/cli/commands/data/index.d.ts +0 -4
- package/dist/cli/commands/data/index.js +0 -62
- package/dist/cli/commands/maintenance/clean.d.ts +0 -2
- package/dist/cli/commands/maintenance/clean.js +0 -24
- package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
- package/dist/cli/commands/maintenance/config-share.js +0 -80
- package/dist/cli/commands/projects/projects.d.ts +0 -2
- package/dist/cli/commands/projects/projects.js +0 -162
- package/dist/cli/commands/storage/contained.d.ts +0 -3
- package/dist/cli/commands/storage/contained.js +0 -179
- package/dist/cli/commands/storage/storage.d.ts +0 -8
- package/dist/cli/commands/storage/storage.js +0 -179
- package/dist/cli/menu/add-agent.d.ts +0 -47
- package/dist/cli/menu/add-agent.js +0 -168
- package/dist/cli/menu/apps.d.ts +0 -69
- package/dist/cli/menu/apps.js +0 -214
- package/dist/cli/menu/data-folder.d.ts +0 -18
- package/dist/cli/menu/data-folder.js +0 -99
- package/dist/cli/menu/share-settings.d.ts +0 -21
- package/dist/cli/menu/share-settings.js +0 -112
- package/dist/cli/menu/vpn-login.d.ts +0 -16
- package/dist/cli/menu/vpn-login.js +0 -74
- package/dist/web/main-NRRS5O6Z.js +0 -157
- /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
- /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
package/sandbox/csharp/MemGov.cs
CHANGED
|
@@ -1,398 +1,668 @@
|
|
|
1
|
-
// memgov.exe: the Windows side of the sandbox's memory governor (src/guest/governor). One long-running process the
|
|
2
|
-
// daemon talks to over stdin/stdout, so the decisions stay in TypeScript and only the Win32 calls live here. C# 5.
|
|
3
|
-
//
|
|
4
|
-
// stdout, a JSON line every --interval seconds (default 2):
|
|
5
|
-
// {"ramTotalMb":..,"ramAvailMb":..,"commitLimitMb":..,"commitAvailMb":..,"procs":[{"pid":..,"name":"rustc","mb":..,"prio":"normal"}]}
|
|
6
|
-
// procs = the processes in the agents' job (the job ptyhost makes for everything the agents start), nothing else.
|
|
7
|
-
// stdin, one command per line:
|
|
8
|
-
// priority <pid> <idle|below|normal> lowers (or restores) the priority of a process in that job
|
|
9
|
-
// suspend <pid> <ms> pauses a process in that job, resumes it after ms (at most 30000)
|
|
10
|
-
// exit
|
|
11
|
-
//
|
|
12
|
-
// It only ever touches processes that are in the agents' job, never itself, and resumes everything it paused when it
|
|
13
|
-
// ends or when the daemon goes away (stdin closes).
|
|
14
|
-
using System;
|
|
15
|
-
using System.Collections.Generic;
|
|
16
|
-
using System.ComponentModel;
|
|
17
|
-
using System.Diagnostics;
|
|
18
|
-
using System.Globalization;
|
|
19
|
-
using System.IO;
|
|
20
|
-
using System.Runtime.InteropServices;
|
|
21
|
-
using System.Text;
|
|
22
|
-
using System.Threading;
|
|
23
|
-
|
|
24
|
-
static class MemGov
|
|
25
|
-
{
|
|
26
|
-
private const string AgentJobName = "Local\\AgentSandboxAgentsJob";
|
|
27
|
-
private const double BytesPerMb = 1024.0 * 1024.0;
|
|
28
|
-
private const int MaxProcesses = 1024;
|
|
29
|
-
private const int MaxSuspendMs = 30000;
|
|
30
|
-
private const uint JobObjectQuery = 0x0004;
|
|
31
|
-
private const int JobObjectBasicProcessIdList = 3;
|
|
32
|
-
private const uint ProcessQueryLimited = 0x1000;
|
|
33
|
-
private const uint ProcessSetInformation = 0x0200;
|
|
34
|
-
private const uint ProcessSuspendResume = 0x0800;
|
|
35
|
-
private const uint IdleClass = 0x00000040;
|
|
36
|
-
private const uint BelowNormalClass = 0x00004000;
|
|
37
|
-
private const uint NormalClass = 0x00000020;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
public
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
public
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
public
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
public
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
public
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
public
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
private static
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
private static string
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
{
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
private static void
|
|
362
|
-
{
|
|
363
|
-
|
|
364
|
-
{
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
1
|
+
// memgov.exe: the Windows side of the sandbox's memory governor (src/guest/governor). One long-running process the
|
|
2
|
+
// daemon talks to over stdin/stdout, so the decisions stay in TypeScript and only the Win32 calls live here. C# 5.
|
|
3
|
+
//
|
|
4
|
+
// stdout, a JSON line every --interval seconds (default 2):
|
|
5
|
+
// {"ramTotalMb":..,"ramAvailMb":..,"commitLimitMb":..,"commitAvailMb":..,"procs":[{"pid":..,"name":"rustc","mb":..,"prio":"normal"}]}
|
|
6
|
+
// procs = the processes in the agents' job (the job ptyhost makes for everything the agents start), nothing else.
|
|
7
|
+
// stdin, one command per line:
|
|
8
|
+
// priority <pid> <idle|below|normal> lowers (or restores) the priority of a process in that job
|
|
9
|
+
// suspend <pid> <ms> pauses a process in that job, resumes it after ms (at most 30000)
|
|
10
|
+
// exit
|
|
11
|
+
//
|
|
12
|
+
// It only ever touches processes that are in the agents' job, never itself, and resumes everything it paused when it
|
|
13
|
+
// ends or when the daemon goes away (stdin closes).
|
|
14
|
+
using System;
|
|
15
|
+
using System.Collections.Generic;
|
|
16
|
+
using System.ComponentModel;
|
|
17
|
+
using System.Diagnostics;
|
|
18
|
+
using System.Globalization;
|
|
19
|
+
using System.IO;
|
|
20
|
+
using System.Runtime.InteropServices;
|
|
21
|
+
using System.Text;
|
|
22
|
+
using System.Threading;
|
|
23
|
+
|
|
24
|
+
static class MemGov
|
|
25
|
+
{
|
|
26
|
+
private const string AgentJobName = "Local\\AgentSandboxAgentsJob";
|
|
27
|
+
private const double BytesPerMb = 1024.0 * 1024.0;
|
|
28
|
+
private const int MaxProcesses = 1024;
|
|
29
|
+
private const int MaxSuspendMs = 30000;
|
|
30
|
+
private const uint JobObjectQuery = 0x0004;
|
|
31
|
+
private const int JobObjectBasicProcessIdList = 3;
|
|
32
|
+
private const uint ProcessQueryLimited = 0x1000;
|
|
33
|
+
private const uint ProcessSetInformation = 0x0200;
|
|
34
|
+
private const uint ProcessSuspendResume = 0x0800;
|
|
35
|
+
private const uint IdleClass = 0x00000040;
|
|
36
|
+
private const uint BelowNormalClass = 0x00004000;
|
|
37
|
+
private const uint NormalClass = 0x00000020;
|
|
38
|
+
// The throttle job: compilers join it from the governor's step 2; its CPU cap follows the dial.
|
|
39
|
+
private const string ThrottleJobName = "Local\\AgentSandboxBuildThrottle";
|
|
40
|
+
private const uint ProcessTerminate = 0x0001;
|
|
41
|
+
private const uint ProcessSetQuota = 0x0100;
|
|
42
|
+
private const int JobObjectCpuRateControlInformation = 15;
|
|
43
|
+
private const uint CpuRateEnable = 0x1;
|
|
44
|
+
private const uint CpuRateHardCap = 0x4;
|
|
45
|
+
private const int ProcessMemoryPriorityClass = 0;
|
|
46
|
+
private const uint MemoryPriorityLow = 2;
|
|
47
|
+
private const uint MemoryPriorityNormal = 5;
|
|
48
|
+
|
|
49
|
+
// JOBOBJECT_CPU_RATE_CONTROL_INFORMATION (CpuRate in hundredths of a percent).
|
|
50
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
51
|
+
private struct CpuRateControl
|
|
52
|
+
{
|
|
53
|
+
public uint ControlFlags;
|
|
54
|
+
public uint CpuRate;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// MEMORYSTATUSEX.
|
|
58
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
59
|
+
private struct MemoryStatus
|
|
60
|
+
{
|
|
61
|
+
public uint Length;
|
|
62
|
+
public uint MemoryLoad;
|
|
63
|
+
public ulong TotalPhys;
|
|
64
|
+
public ulong AvailPhys;
|
|
65
|
+
public ulong TotalPageFile;
|
|
66
|
+
public ulong AvailPageFile;
|
|
67
|
+
public ulong TotalVirtual;
|
|
68
|
+
public ulong AvailVirtual;
|
|
69
|
+
public ulong AvailExtendedVirtual;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// PROCESS_MEMORY_COUNTERS_EX (PrivateUsage is the private memory Process.PrivateMemorySize64 reported).
|
|
73
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
74
|
+
private struct MemoryCounters
|
|
75
|
+
{
|
|
76
|
+
public uint Size;
|
|
77
|
+
public uint PageFaultCount;
|
|
78
|
+
public UIntPtr PeakWorkingSetSize;
|
|
79
|
+
public UIntPtr WorkingSetSize;
|
|
80
|
+
public UIntPtr QuotaPeakPagedPoolUsage;
|
|
81
|
+
public UIntPtr QuotaPagedPoolUsage;
|
|
82
|
+
public UIntPtr QuotaPeakNonPagedPoolUsage;
|
|
83
|
+
public UIntPtr QuotaNonPagedPoolUsage;
|
|
84
|
+
public UIntPtr PagefileUsage;
|
|
85
|
+
public UIntPtr PeakPagefileUsage;
|
|
86
|
+
public UIntPtr PrivateUsage;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private static class NativeMethods
|
|
90
|
+
{
|
|
91
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
92
|
+
public static extern bool GlobalMemoryStatusEx(ref MemoryStatus status);
|
|
93
|
+
|
|
94
|
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
95
|
+
public static extern IntPtr OpenJobObjectW(uint access, bool inherit, string name);
|
|
96
|
+
|
|
97
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
98
|
+
public static extern bool QueryInformationJobObject(IntPtr job, int infoClass, IntPtr info, int length, IntPtr returnLength);
|
|
99
|
+
|
|
100
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
101
|
+
public static extern bool IsProcessInJob(IntPtr process, IntPtr job, out bool result);
|
|
102
|
+
|
|
103
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
104
|
+
public static extern IntPtr OpenProcess(uint access, bool inherit, int pid);
|
|
105
|
+
|
|
106
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
107
|
+
public static extern bool SetPriorityClass(IntPtr process, uint priorityClass);
|
|
108
|
+
|
|
109
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
110
|
+
public static extern uint GetPriorityClass(IntPtr process);
|
|
111
|
+
|
|
112
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
113
|
+
public static extern bool CloseHandle(IntPtr handle);
|
|
114
|
+
|
|
115
|
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
116
|
+
public static extern IntPtr CreateJobObjectW(IntPtr attributes, string name);
|
|
117
|
+
|
|
118
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
119
|
+
public static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
|
|
120
|
+
|
|
121
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
122
|
+
public static extern bool SetInformationJobObject(IntPtr job, int infoClass, ref CpuRateControl info, int length);
|
|
123
|
+
|
|
124
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
125
|
+
public static extern bool SetProcessInformation(IntPtr process, int infoClass, ref uint info, int length);
|
|
126
|
+
|
|
127
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
128
|
+
public static extern bool SetProcessWorkingSetSize(IntPtr process, IntPtr minimum, IntPtr maximum);
|
|
129
|
+
|
|
130
|
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
131
|
+
public static extern bool QueryFullProcessImageNameW(IntPtr process, uint flags, StringBuilder name, ref uint size);
|
|
132
|
+
|
|
133
|
+
[DllImport("kernel32.dll", EntryPoint = "K32GetProcessMemoryInfo", SetLastError = true)]
|
|
134
|
+
public static extern bool GetProcessMemoryInfo(IntPtr process, ref MemoryCounters counters, uint size);
|
|
135
|
+
|
|
136
|
+
[DllImport("ntdll.dll")]
|
|
137
|
+
public static extern int NtSuspendProcess(IntPtr process);
|
|
138
|
+
|
|
139
|
+
[DllImport("ntdll.dll")]
|
|
140
|
+
public static extern int NtResumeProcess(IntPtr process);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private static readonly object Gate = new object();
|
|
144
|
+
private static readonly Dictionary<int, DateTime> Paused = new Dictionary<int, DateTime>();
|
|
145
|
+
|
|
146
|
+
/// <summary>The paused process ids on disk, so a memgov that is killed mid-pause is undone by the next one.</summary>
|
|
147
|
+
private static readonly string HeldFile = Path.Combine(Path.GetTempPath(), "agent-sandbox-memgov-held.txt");
|
|
148
|
+
private static IntPtr job = IntPtr.Zero;
|
|
149
|
+
private static IntPtr throttleJob = IntPtr.Zero;
|
|
150
|
+
private static volatile bool stop;
|
|
151
|
+
|
|
152
|
+
static int Main(string[] args)
|
|
153
|
+
{
|
|
154
|
+
if (args.Length == 0 || args[0] != "serve")
|
|
155
|
+
{
|
|
156
|
+
Console.Error.WriteLine("usage: memgov serve [--interval seconds]");
|
|
157
|
+
return 2;
|
|
158
|
+
}
|
|
159
|
+
int interval = 2;
|
|
160
|
+
for (int i = 1; i + 1 < args.Length; i++)
|
|
161
|
+
{
|
|
162
|
+
if (args[i] == "--interval")
|
|
163
|
+
{
|
|
164
|
+
int.TryParse(args[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out interval);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (interval < 1)
|
|
168
|
+
{
|
|
169
|
+
interval = 2;
|
|
170
|
+
}
|
|
171
|
+
// A memgov that ended without resuming (killed, crashed) left its pauses in the file: undo them first.
|
|
172
|
+
RecoverHeld();
|
|
173
|
+
// A memgov that ended while compilers were capped left the cap: lift it before anything else.
|
|
174
|
+
SetCpuRate(100);
|
|
175
|
+
Thread commands = new Thread(ReadCommands);
|
|
176
|
+
commands.IsBackground = true;
|
|
177
|
+
commands.Start();
|
|
178
|
+
Thread resumer = new Thread(ResumeWhenDue);
|
|
179
|
+
resumer.IsBackground = true;
|
|
180
|
+
resumer.Start();
|
|
181
|
+
while (!stop)
|
|
182
|
+
{
|
|
183
|
+
try
|
|
184
|
+
{
|
|
185
|
+
Console.Out.WriteLine(Sample());
|
|
186
|
+
Console.Out.Flush();
|
|
187
|
+
}
|
|
188
|
+
catch (Exception)
|
|
189
|
+
{
|
|
190
|
+
// A sample that fails (a process ended while it was read) is skipped; the next one tries again.
|
|
191
|
+
}
|
|
192
|
+
for (int waited = 0; waited < interval * 10 && !stop; waited++)
|
|
193
|
+
{
|
|
194
|
+
Thread.Sleep(100);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
ResumeAll();
|
|
198
|
+
SetCpuRate(100);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// <summary>The self-test's own job (MEMGOV_JOB_NAME, a name in the agents' job family), or null.</summary>
|
|
203
|
+
private static string TestJobName()
|
|
204
|
+
{
|
|
205
|
+
string name = Environment.GetEnvironmentVariable("MEMGOV_JOB_NAME");
|
|
206
|
+
return !string.IsNullOrEmpty(name) && name.StartsWith(AgentJobName + "Test", StringComparison.Ordinal) ? name : null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private static string JobName()
|
|
210
|
+
{
|
|
211
|
+
return TestJobName() ?? AgentJobName;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// <summary>The job, opened once it exists (ptyhost makes it when the first agent starts).</summary>
|
|
215
|
+
private static IntPtr Job()
|
|
216
|
+
{
|
|
217
|
+
if (job == IntPtr.Zero)
|
|
218
|
+
{
|
|
219
|
+
job = NativeMethods.OpenJobObjectW(JobObjectQuery, false, JobName());
|
|
220
|
+
}
|
|
221
|
+
return job;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private static string Sample()
|
|
225
|
+
{
|
|
226
|
+
MemoryStatus status = new MemoryStatus();
|
|
227
|
+
status.Length = (uint)Marshal.SizeOf(typeof(MemoryStatus));
|
|
228
|
+
if (!NativeMethods.GlobalMemoryStatusEx(ref status))
|
|
229
|
+
{
|
|
230
|
+
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
231
|
+
}
|
|
232
|
+
StringBuilder sb = new StringBuilder();
|
|
233
|
+
sb.Append("{\"ramTotalMb\":").Append(Mb(status.TotalPhys));
|
|
234
|
+
sb.Append(",\"ramAvailMb\":").Append(Mb(status.AvailPhys));
|
|
235
|
+
sb.Append(",\"commitLimitMb\":").Append(Mb(status.TotalPageFile));
|
|
236
|
+
sb.Append(",\"commitAvailMb\":").Append(Mb(status.AvailPageFile));
|
|
237
|
+
sb.Append(",\"procs\":[");
|
|
238
|
+
bool first = true;
|
|
239
|
+
int self = SelfId;
|
|
240
|
+
foreach (int pid in JobProcessIds())
|
|
241
|
+
{
|
|
242
|
+
if (pid == self)
|
|
243
|
+
{
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
// Read everything first: a process that ends half way must not leave half an entry behind.
|
|
247
|
+
string entry = Entry(pid);
|
|
248
|
+
if (entry == null)
|
|
249
|
+
{
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (!first)
|
|
253
|
+
{
|
|
254
|
+
sb.Append(',');
|
|
255
|
+
}
|
|
256
|
+
first = false;
|
|
257
|
+
sb.Append(entry);
|
|
258
|
+
}
|
|
259
|
+
return sb.Append("]}").ToString();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private static readonly int SelfId = Process.GetCurrentProcess().Id;
|
|
263
|
+
|
|
264
|
+
/// <summary>
|
|
265
|
+
/// One process's line, read through one handle: its name, private memory and priority (null when it ended). Not
|
|
266
|
+
/// System.Diagnostics.Process, which reads the whole system's process list for every process it opens: with a
|
|
267
|
+
/// build's dozens of processes every two seconds that alone kept a CPU busy.
|
|
268
|
+
/// </summary>
|
|
269
|
+
private static string Entry(int pid)
|
|
270
|
+
{
|
|
271
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessQueryLimited, false, pid);
|
|
272
|
+
if (process == IntPtr.Zero)
|
|
273
|
+
{
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
try
|
|
277
|
+
{
|
|
278
|
+
StringBuilder path = new StringBuilder(1024);
|
|
279
|
+
uint size = (uint)path.Capacity;
|
|
280
|
+
if (!NativeMethods.QueryFullProcessImageNameW(process, 0, path, ref size))
|
|
281
|
+
{
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
MemoryCounters counters = new MemoryCounters();
|
|
285
|
+
counters.Size = (uint)Marshal.SizeOf(typeof(MemoryCounters));
|
|
286
|
+
if (!NativeMethods.GetProcessMemoryInfo(process, ref counters, counters.Size))
|
|
287
|
+
{
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
uint c = NativeMethods.GetPriorityClass(process);
|
|
291
|
+
string priority = c == IdleClass ? "idle" : c == BelowNormalClass ? "below" : "normal";
|
|
292
|
+
return "{\"pid\":" + pid.ToString(CultureInfo.InvariantCulture)
|
|
293
|
+
+ ",\"name\":" + Json(Path.GetFileNameWithoutExtension(path.ToString(0, (int)size)))
|
|
294
|
+
+ ",\"mb\":" + Mb(counters.PrivateUsage.ToUInt64())
|
|
295
|
+
+ ",\"prio\":\"" + priority + "\"}";
|
|
296
|
+
}
|
|
297
|
+
finally
|
|
298
|
+
{
|
|
299
|
+
NativeMethods.CloseHandle(process);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private static List<int> JobProcessIds()
|
|
304
|
+
{
|
|
305
|
+
List<int> ids = new List<int>();
|
|
306
|
+
IntPtr handle = Job();
|
|
307
|
+
if (handle == IntPtr.Zero)
|
|
308
|
+
{
|
|
309
|
+
return ids;
|
|
310
|
+
}
|
|
311
|
+
// JOBOBJECT_BASIC_PROCESS_ID_LIST: two counts, then the ids (pointer-sized).
|
|
312
|
+
int length = 8 + IntPtr.Size * MaxProcesses;
|
|
313
|
+
IntPtr buffer = Marshal.AllocHGlobal(length);
|
|
314
|
+
try
|
|
315
|
+
{
|
|
316
|
+
if (!NativeMethods.QueryInformationJobObject(handle, JobObjectBasicProcessIdList, buffer, length, IntPtr.Zero))
|
|
317
|
+
{
|
|
318
|
+
return ids;
|
|
319
|
+
}
|
|
320
|
+
int count = Marshal.ReadInt32(buffer, 4);
|
|
321
|
+
for (int i = 0; i < count && i < MaxProcesses; i++)
|
|
322
|
+
{
|
|
323
|
+
ids.Add((int)Marshal.ReadIntPtr(buffer, 8 + i * IntPtr.Size).ToInt64());
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
finally
|
|
327
|
+
{
|
|
328
|
+
Marshal.FreeHGlobal(buffer);
|
|
329
|
+
}
|
|
330
|
+
return ids;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/// <summary>Whether pid is a process of the agents' job and not this one: the only ones this program touches.</summary>
|
|
334
|
+
private static bool Allowed(int pid)
|
|
335
|
+
{
|
|
336
|
+
return pid != SelfId && JobProcessIds().Contains(pid);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private static void ReadCommands()
|
|
340
|
+
{
|
|
341
|
+
string line;
|
|
342
|
+
while ((line = Console.In.ReadLine()) != null)
|
|
343
|
+
{
|
|
344
|
+
try
|
|
345
|
+
{
|
|
346
|
+
Run(line.Trim().Split(' '));
|
|
347
|
+
}
|
|
348
|
+
catch (Exception)
|
|
349
|
+
{
|
|
350
|
+
// A bad command is ignored.
|
|
351
|
+
}
|
|
352
|
+
if (stop)
|
|
353
|
+
{
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// The daemon is gone: put everything back and end.
|
|
358
|
+
stop = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private static void Run(string[] parts)
|
|
362
|
+
{
|
|
363
|
+
if (parts.Length == 1 && parts[0] == "exit")
|
|
364
|
+
{
|
|
365
|
+
stop = true;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (parts.Length == 2 && parts[0] == "cpurate")
|
|
369
|
+
{
|
|
370
|
+
int percent;
|
|
371
|
+
if (int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out percent))
|
|
372
|
+
{
|
|
373
|
+
SetCpuRate(percent);
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
int pid;
|
|
378
|
+
if (parts.Length < 2 || !int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out pid) || !Allowed(pid))
|
|
379
|
+
{
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (parts[0] == "throttle")
|
|
383
|
+
{
|
|
384
|
+
Throttle(pid);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (parts[0] == "trim")
|
|
388
|
+
{
|
|
389
|
+
Trim(pid);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (parts.Length < 3)
|
|
393
|
+
{
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (parts[0] == "memprio")
|
|
397
|
+
{
|
|
398
|
+
SetMemoryPriority(pid, parts[2] == "low" ? MemoryPriorityLow : MemoryPriorityNormal);
|
|
399
|
+
}
|
|
400
|
+
else if (parts[0] == "priority")
|
|
401
|
+
{
|
|
402
|
+
uint cls = parts[2] == "idle" ? IdleClass : parts[2] == "below" ? BelowNormalClass : NormalClass;
|
|
403
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSetInformation, false, pid);
|
|
404
|
+
if (process != IntPtr.Zero)
|
|
405
|
+
{
|
|
406
|
+
NativeMethods.SetPriorityClass(process, cls);
|
|
407
|
+
NativeMethods.CloseHandle(process);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else if (parts[0] == "suspend")
|
|
411
|
+
{
|
|
412
|
+
int ms;
|
|
413
|
+
if (!int.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out ms) || ms < 1)
|
|
414
|
+
{
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
Suspend(pid, Math.Min(ms, MaxSuspendMs));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/// <summary>The throttle job, made once. A process already in the agents' job is nested in it (Windows 8+).</summary>
|
|
422
|
+
private static IntPtr ThrottleJob()
|
|
423
|
+
{
|
|
424
|
+
if (throttleJob == IntPtr.Zero)
|
|
425
|
+
{
|
|
426
|
+
string test = TestJobName();
|
|
427
|
+
throttleJob = NativeMethods.CreateJobObjectW(IntPtr.Zero, test != null ? test + "Throttle" : ThrottleJobName);
|
|
428
|
+
}
|
|
429
|
+
return throttleJob;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/// <summary>The CPU cap of the compilers in the throttle job, percent of the sandbox; 100 or more lifts it.</summary>
|
|
433
|
+
private static void SetCpuRate(int percent)
|
|
434
|
+
{
|
|
435
|
+
IntPtr throttle = ThrottleJob();
|
|
436
|
+
if (throttle == IntPtr.Zero)
|
|
437
|
+
{
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
CpuRateControl info = new CpuRateControl();
|
|
441
|
+
if (percent < 100)
|
|
442
|
+
{
|
|
443
|
+
info.ControlFlags = CpuRateEnable | CpuRateHardCap;
|
|
444
|
+
info.CpuRate = (uint)(Math.Max(1, percent) * 100);
|
|
445
|
+
}
|
|
446
|
+
NativeMethods.SetInformationJobObject(throttle, JobObjectCpuRateControlInformation, ref info, Marshal.SizeOf(typeof(CpuRateControl)));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private static void Throttle(int pid)
|
|
450
|
+
{
|
|
451
|
+
IntPtr throttle = ThrottleJob();
|
|
452
|
+
if (throttle == IntPtr.Zero)
|
|
453
|
+
{
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSetQuota | ProcessTerminate, false, pid);
|
|
457
|
+
if (process == IntPtr.Zero)
|
|
458
|
+
{
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
try
|
|
462
|
+
{
|
|
463
|
+
NativeMethods.AssignProcessToJobObject(throttle, process);
|
|
464
|
+
}
|
|
465
|
+
finally
|
|
466
|
+
{
|
|
467
|
+
NativeMethods.CloseHandle(process);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/// <summary>Low memory priority: Windows trims this process's pages before others' (the agents keep theirs).</summary>
|
|
472
|
+
private static void SetMemoryPriority(int pid, uint priority)
|
|
473
|
+
{
|
|
474
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSetInformation, false, pid);
|
|
475
|
+
if (process == IntPtr.Zero)
|
|
476
|
+
{
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
try
|
|
480
|
+
{
|
|
481
|
+
uint value = priority;
|
|
482
|
+
NativeMethods.SetProcessInformation(process, ProcessMemoryPriorityClass, ref value, sizeof(uint));
|
|
483
|
+
}
|
|
484
|
+
finally
|
|
485
|
+
{
|
|
486
|
+
NativeMethods.CloseHandle(process);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/// <summary>A waiting compiler's pages go to the paging file now, so the ones that run have the RAM.</summary>
|
|
491
|
+
private static void Trim(int pid)
|
|
492
|
+
{
|
|
493
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSetQuota, false, pid);
|
|
494
|
+
if (process == IntPtr.Zero)
|
|
495
|
+
{
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
try
|
|
499
|
+
{
|
|
500
|
+
NativeMethods.SetProcessWorkingSetSize(process, new IntPtr(-1), new IntPtr(-1));
|
|
501
|
+
}
|
|
502
|
+
finally
|
|
503
|
+
{
|
|
504
|
+
NativeMethods.CloseHandle(process);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private static void Suspend(int pid, int ms)
|
|
509
|
+
{
|
|
510
|
+
lock (Gate)
|
|
511
|
+
{
|
|
512
|
+
if (Paused.ContainsKey(pid))
|
|
513
|
+
{
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSuspendResume, false, pid);
|
|
517
|
+
if (process == IntPtr.Zero)
|
|
518
|
+
{
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
try
|
|
522
|
+
{
|
|
523
|
+
// Recorded before the pause, so a memgov that dies right after still lets the next one resume it.
|
|
524
|
+
Paused[pid] = DateTime.UtcNow.AddMilliseconds(ms);
|
|
525
|
+
SaveHeld();
|
|
526
|
+
NativeMethods.NtSuspendProcess(process);
|
|
527
|
+
}
|
|
528
|
+
finally
|
|
529
|
+
{
|
|
530
|
+
NativeMethods.CloseHandle(process);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private static void Resume(int pid)
|
|
536
|
+
{
|
|
537
|
+
IntPtr process = NativeMethods.OpenProcess(ProcessSuspendResume, false, pid);
|
|
538
|
+
if (process != IntPtr.Zero)
|
|
539
|
+
{
|
|
540
|
+
NativeMethods.NtResumeProcess(process);
|
|
541
|
+
NativeMethods.CloseHandle(process);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/// <summary>A paused process always comes back: when its time is up (checked every 100 ms).</summary>
|
|
546
|
+
private static void ResumeWhenDue()
|
|
547
|
+
{
|
|
548
|
+
while (true)
|
|
549
|
+
{
|
|
550
|
+
Thread.Sleep(100);
|
|
551
|
+
lock (Gate)
|
|
552
|
+
{
|
|
553
|
+
List<int> due = new List<int>();
|
|
554
|
+
foreach (KeyValuePair<int, DateTime> p in Paused)
|
|
555
|
+
{
|
|
556
|
+
if (p.Value <= DateTime.UtcNow)
|
|
557
|
+
{
|
|
558
|
+
due.Add(p.Key);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
foreach (int pid in due)
|
|
562
|
+
{
|
|
563
|
+
Resume(pid);
|
|
564
|
+
Paused.Remove(pid);
|
|
565
|
+
}
|
|
566
|
+
if (due.Count > 0)
|
|
567
|
+
{
|
|
568
|
+
SaveHeld();
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
private static void ResumeAll()
|
|
575
|
+
{
|
|
576
|
+
lock (Gate)
|
|
577
|
+
{
|
|
578
|
+
foreach (int pid in new List<int>(Paused.Keys))
|
|
579
|
+
{
|
|
580
|
+
Resume(pid);
|
|
581
|
+
}
|
|
582
|
+
Paused.Clear();
|
|
583
|
+
SaveHeld();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// <summary>The processes paused right now, one id per line (empty: the file goes). Called with Gate held.</summary>
|
|
588
|
+
private static void SaveHeld()
|
|
589
|
+
{
|
|
590
|
+
try
|
|
591
|
+
{
|
|
592
|
+
if (Paused.Count == 0)
|
|
593
|
+
{
|
|
594
|
+
File.Delete(HeldFile);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
List<string> lines = new List<string>();
|
|
598
|
+
foreach (int pid in Paused.Keys)
|
|
599
|
+
{
|
|
600
|
+
lines.Add(pid.ToString(CultureInfo.InvariantCulture));
|
|
601
|
+
}
|
|
602
|
+
File.WriteAllLines(HeldFile, lines.ToArray());
|
|
603
|
+
}
|
|
604
|
+
catch (IOException)
|
|
605
|
+
{
|
|
606
|
+
// Best effort: the in-memory list still resumes every pause while this memgov runs.
|
|
607
|
+
}
|
|
608
|
+
catch (UnauthorizedAccessException)
|
|
609
|
+
{
|
|
610
|
+
// Same as above.
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/// <summary>Resumes what an earlier memgov paused and never resumed. Resuming a running process changes nothing.</summary>
|
|
615
|
+
private static void RecoverHeld()
|
|
616
|
+
{
|
|
617
|
+
try
|
|
618
|
+
{
|
|
619
|
+
if (!File.Exists(HeldFile))
|
|
620
|
+
{
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
foreach (string line in File.ReadAllLines(HeldFile))
|
|
624
|
+
{
|
|
625
|
+
int pid;
|
|
626
|
+
if (int.TryParse(line.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out pid) && pid > 0)
|
|
627
|
+
{
|
|
628
|
+
Resume(pid);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
File.Delete(HeldFile);
|
|
632
|
+
}
|
|
633
|
+
catch (IOException)
|
|
634
|
+
{
|
|
635
|
+
// Nothing more to do: the file stays and the next start tries again.
|
|
636
|
+
}
|
|
637
|
+
catch (UnauthorizedAccessException)
|
|
638
|
+
{
|
|
639
|
+
// Same as above.
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
private static string Mb(ulong bytes)
|
|
644
|
+
{
|
|
645
|
+
return Math.Floor(bytes / BytesPerMb).ToString("0", CultureInfo.InvariantCulture);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private static string Json(string value)
|
|
649
|
+
{
|
|
650
|
+
StringBuilder sb = new StringBuilder("\"");
|
|
651
|
+
foreach (char c in value)
|
|
652
|
+
{
|
|
653
|
+
if (c == '"' || c == '\\')
|
|
654
|
+
{
|
|
655
|
+
sb.Append('\\').Append(c);
|
|
656
|
+
}
|
|
657
|
+
else if (c < 0x20)
|
|
658
|
+
{
|
|
659
|
+
sb.Append("\\u").Append(((int)c).ToString("x4", CultureInfo.InvariantCulture));
|
|
660
|
+
}
|
|
661
|
+
else
|
|
662
|
+
{
|
|
663
|
+
sb.Append(c);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return sb.Append('"').ToString();
|
|
667
|
+
}
|
|
668
|
+
}
|