diff --git a/packages/SwingSet/src/controller.js b/packages/SwingSet/src/controller.js index c7c3753a593..57abca464dc 100644 --- a/packages/SwingSet/src/controller.js +++ b/packages/SwingSet/src/controller.js @@ -5,7 +5,7 @@ import path from 'path'; import process from 'process'; import re2 from 're2'; import { performance } from 'perf_hooks'; -import { spawn } from 'child_process'; +import { spawn as ambientSpawn } from 'child_process'; import { type as osType } from 'os'; import { Worker } from 'worker_threads'; import anylogger from 'anylogger'; @@ -46,9 +46,13 @@ function unhandledRejectionHandler(e) { /** * @param {{ moduleFormat: string, source: string }[]} bundles - * @param {{ snapstorePath?: string, env: Record }} opts + * @param {{ + * snapstorePath?: string, + * spawn: typeof import('child_process').spawn + * env: Record, + * }} opts */ -export function makeStartXSnap(bundles, { snapstorePath, env }) { +export function makeStartXSnap(bundles, { snapstorePath, env, spawn }) { /** @type { import('@agoric/xsnap/src/xsnap').XSnapOptions } */ const xsnapOpts = { os: osType(), @@ -79,8 +83,9 @@ export function makeStartXSnap(bundles, { snapstorePath, env }) { /** * @param {string} name * @param {(request: Uint8Array) => Promise} handleCommand + * @param { boolean } [metered] */ - async function startXSnap(name, handleCommand) { + async function startXSnap(name, handleCommand, metered) { if (supervisorHash) { return snapStore.load(supervisorHash, async snapshot => { const xs = xsnap({ snapshot, name, handleCommand, ...xsnapOpts }); @@ -88,7 +93,8 @@ export function makeStartXSnap(bundles, { snapstorePath, env }) { return xs; }); } - const worker = xsnap({ handleCommand, name, ...xsnapOpts }); + const meterOpts = metered ? {} : { meteringLimit: 0 }; + const worker = xsnap({ handleCommand, name, ...meterOpts, ...xsnapOpts }); for (const bundle of bundles) { assert( @@ -117,6 +123,7 @@ export function makeStartXSnap(bundles, { snapstorePath, env }) { * slogFile?: string, * testTrackDecref?: unknown, * snapstorePath?: string, + * spawn?: typeof import('child_process').spawn, * env?: Record * }} runtimeOptions */ @@ -137,6 +144,7 @@ export async function makeSwingsetController( slogCallbacks, slogFile, snapstorePath, + spawn = ambientSpawn, } = runtimeOptions; if (typeof Compartment === 'undefined') { throw Error('SES must be installed before calling makeSwingsetController'); @@ -271,7 +279,7 @@ export async function makeSwingsetController( // @ts-ignore assume supervisorBundle is set JSON.parse(hostStorage.get('supervisorBundle')), ]; - const startXSnap = makeStartXSnap(bundles, { snapstorePath, env }); + const startXSnap = makeStartXSnap(bundles, { snapstorePath, env, spawn }); const kernelEndowments = { waitUntilQuiescent, diff --git a/packages/SwingSet/src/kernel/vatManager/manager-subprocess-xsnap.js b/packages/SwingSet/src/kernel/vatManager/manager-subprocess-xsnap.js index a579e306ed6..df0168a9e3a 100644 --- a/packages/SwingSet/src/kernel/vatManager/manager-subprocess-xsnap.js +++ b/packages/SwingSet/src/kernel/vatManager/manager-subprocess-xsnap.js @@ -20,7 +20,7 @@ const decoder = new TextDecoder(); * allVatPowers: VatPowers, * kernelKeeper: KernelKeeper, * kernelSlog: KernelSlog, - * startXSnap: (name: string, handleCommand: SyncHandler) => Promise, + * startXSnap: (name: string, handleCommand: SyncHandler, metered?: boolean) => Promise, * testLog: (...args: unknown[]) => void, * }} tools * @returns { VatManagerFactory } @@ -53,6 +53,7 @@ export function makeXsSubprocessFactory({ virtualObjectCacheSize, enableDisavow, name, + metered, } = managerOptions; assert( !managerOptions.enableSetup, @@ -101,7 +102,7 @@ export function makeXsSubprocessFactory({ } // start the worker and establish a connection - const worker = await startXSnap(`${vatID}:${name}`, handleCommand); + const worker = await startXSnap(`${vatID}:${name}`, handleCommand, metered); /** @type { (item: Tagged) => Promise } */ async function issueTagged(item) { diff --git a/packages/SwingSet/test/test-controller.js b/packages/SwingSet/test/test-controller.js index 22b088eee0b..56c74d5e1b2 100644 --- a/packages/SwingSet/test/test-controller.js +++ b/packages/SwingSet/test/test-controller.js @@ -3,8 +3,14 @@ import { test } from '../tools/prepare-test-env-ava'; import path from 'path'; +import { spawn } from 'child_process'; import { initSwingStore } from '@agoric/swing-store-simple'; -import { buildVatController, loadBasedir } from '../src/index'; +import { + buildVatController, + loadBasedir, + makeSwingsetController, +} from '../src/index'; +import { initializeSwingset } from '../src/initializeSwingset'; import { checkKT } from './util'; function capdata(body, slots = []) { @@ -117,6 +123,28 @@ test('XS bootstrap', async t => { ); }); +test('static vats are unmetered on XS', async t => { + const hostStorage = initSwingStore().storage; + const config = await loadBasedir( + path.resolve(__dirname, 'basedir-controller-2'), + ); + config.defaultManagerType = 'xs-worker'; + await initializeSwingset(config, [], hostStorage); + const limited = []; + const c = await makeSwingsetController( + hostStorage, + {}, + { + spawn(command, args, options) { + limited.push(args.includes('-l')); + return spawn(command, args, options); + }, + }, + ); + t.deepEqual(c.dump().log, ['bootstrap called']); + t.deepEqual(limited, [false, false, false]); +}); + test('validate config.defaultManagerType', async t => { const config = await loadBasedir( path.resolve(__dirname, 'basedir-controller-2'), diff --git a/packages/xsnap/test/test-xs-perf.js b/packages/xsnap/test/test-xs-perf.js index d2d36d626b9..1fd6a9d973b 100644 --- a/packages/xsnap/test/test-xs-perf.js +++ b/packages/xsnap/test/test-xs-perf.js @@ -71,6 +71,22 @@ test('meter details', async t => { t.is(meterType, 'xs-meter-7'); }); +test('meter details are still available with no limit', async t => { + const opts = options(); + const vat = xsnap({ ...opts, meteringLimit: 0 }); + t.teardown(() => vat.terminate()); + const result = await vat.evaluate(` + for (ix = 0; ix < 200; ix++) { + } + `); + const { meterUsage: meters } = result; + t.log(meters); + t.is(typeof meters.compute, 'number'); + t.is(typeof meters.allocate, 'number'); + t.true(meters.compute > 0); + t.true(meters.allocate > 0); +}); + test('high resolution timer', async t => { const opts = options(); const vat = xsnap(opts);