Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor startXSnap #7235

Merged
merged 8 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/SwingSet/misc-tools/replay-transcript.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { makeSnapStore } from '@agoric/swing-store';
import { entryPaths as lockdownEntryPaths } from '@agoric/xsnap-lockdown/src/paths.js';
import { entryPaths as supervisorEntryPaths } from '@agoric/swingset-xsnap-supervisor/src/paths.js';
import { waitUntilQuiescent } from '../src/lib-nodejs/waitUntilQuiescent.js';
import { makeStartXSnap } from '../src/controller/controller.js';
import { makeStartXSnap } from '../src/controller/startXSnap.js';
import { makeXsSubprocessFactory } from '../src/kernel/vat-loader/manager-subprocess-xsnap.js';
import { makeLocalVatManagerFactory } from '../src/kernel/vat-loader/manager-local.js';
import { requireIdentical } from '../src/kernel/vat-loader/transcript.js';
Expand Down Expand Up @@ -152,10 +152,6 @@ async function replay(transcriptFile) {
JSON.parse(fs.readFileSync('lockdown-bundle')),
JSON.parse(fs.readFileSync('supervisor-bundle')),
];
const env = {};
if (RECORD_XSNAP_TRACE) {
env.XSNAP_TEST_RECORD = process.cwd();
}

const capturePIDSpawn = (...args) => {
const child = spawn(...args);
Expand All @@ -164,8 +160,8 @@ async function replay(transcriptFile) {
};
const startXSnap = makeStartXSnap(bundles, {
snapStore,
env,
spawn: capturePIDSpawn,
workerTraceRootPath: RECORD_XSNAP_TRACE ? process.cwd() : undefined,
});
factory = makeXsSubprocessFactory({
kernelKeeper: fakeKernelKeeper,
Expand Down
1 change: 1 addition & 0 deletions packages/SwingSet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"lint:eslint": "eslint ."
},
"devDependencies": {
"@endo/far": "^0.2.14",
"@types/better-sqlite3": "^7.5.0",
"@types/microtime": "^2.1.0",
"@types/tmp": "^0.2.0",
Expand Down
89 changes: 3 additions & 86 deletions packages/SwingSet/src/controller/controller.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
/* global globalThis, WeakRef, FinalizationRegistry */
/* eslint-disable @typescript-eslint/prefer-ts-expect-error -- https://github.com/Agoric/agoric-sdk/issues/4620 */

import fs from 'fs';
import path from 'path';
import process from 'process';
import crypto from 'crypto';
import { performance } from 'perf_hooks';
import { spawn as ambientSpawn } from 'child_process';
import { type as osType } from 'os';
import anylogger from 'anylogger';
import microtime from 'microtime';

import { assert, Fail } from '@agoric/assert';
import { importBundle } from '@endo/import-bundle';
import { xsnap, recordXSnap } from '@agoric/xsnap';
import { initSwingStore } from '@agoric/swing-store';

import { checkBundle } from '@endo/check-bundle/lite.js';
Expand All @@ -27,8 +23,7 @@ import {
swingsetIsInitialized,
initializeSwingset,
} from './initializeSwingset.js';

const NETSTRING_MAX_CHUNK_SIZE = 12_000_000;
import { makeStartXSnap } from './startXSnap.js';

/** @param {Uint8Array} bytes */
export function computeSha512(bytes) {
Expand Down Expand Up @@ -77,85 +72,6 @@ function unhandledRejectionHandler(e, pr) {
console.error('UnhandledPromiseRejectionWarning:', e);
}

/**
* @param {{ moduleFormat: string, source: string }[]} bundles
* @param {{
* snapStore?: SnapStore,
* spawn: typeof import('child_process').spawn
* env: Record<string, string | undefined>,
* }} opts
*/
export function makeStartXSnap(bundles, { snapStore, env, spawn }) {
/** @type { import('@agoric/xsnap/src/xsnap').XSnapOptions } */
const xsnapOpts = {
os: osType(),
spawn,
stdout: 'inherit',
stderr: 'inherit',
debug: !!env.XSNAP_DEBUG,
netstringMaxChunkSize: NETSTRING_MAX_CHUNK_SIZE,
};

let doXSnap = xsnap;
const { XSNAP_TEST_RECORD } = env;
if (XSNAP_TEST_RECORD) {
console.log('SwingSet xs-worker tracing:', { XSNAP_TEST_RECORD });
let serial = 0;
doXSnap = opts => {
const workerTrace =
path.resolve(`${XSNAP_TEST_RECORD}/${serial}`) + path.sep;
serial += 1;
fs.mkdirSync(workerTrace, { recursive: true });
return recordXSnap(opts, workerTrace, {
writeFileSync: fs.writeFileSync,
});
};
}

/**
* @param {string} vatID
* @param {string} name
* @param {(request: Uint8Array) => Promise<Uint8Array>} handleCommand
* @param {boolean} [metered]
* @param {boolean} [reload]
*/
async function startXSnap(
vatID,
name,
handleCommand,
metered,
reload = false,
) {
const meterOpts = metered ? {} : { meteringLimit: 0 };
if (snapStore && reload) {
// console.log('startXSnap from', { snapshotHash });
return snapStore.loadSnapshot(vatID, async snapshot => {
const xs = doXSnap({
snapshot,
name,
handleCommand,
...meterOpts,
...xsnapOpts,
});
await xs.isReady();
return xs;
});
}
// console.log('fresh xsnap', { snapStore: snapStore });
const worker = doXSnap({ handleCommand, name, ...meterOpts, ...xsnapOpts });

for (const bundle of bundles) {
bundle.moduleFormat === 'getExport' ||
bundle.moduleFormat === 'nestedEvaluate' ||
Fail`unexpected: ${bundle.moduleFormat}`;
// eslint-disable-next-line no-await-in-loop, @jessie.js/no-nested-await
await worker.evaluate(`(${bundle.source}\n)()`.trim());
}
return worker;
}
return startXSnap;
}

/**
*
* @param {SwingStoreKernelStorage} kernelStorage
Expand Down Expand Up @@ -272,8 +188,9 @@ export async function makeSwingsetController(
];
const startXSnap = makeStartXSnap(bundles, {
snapStore: kernelStorage.snapStore,
env,
spawn,
debug: !!env.XSNAP_DEBUG,
workerTraceRootPath: env.XSNAP_TEST_RECORD,
});

const kernelEndowments = {
Expand Down
8 changes: 7 additions & 1 deletion packages/SwingSet/src/controller/initializeKernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ function makeVatRootObjectSlot() {
return makeVatSlot('object', true, 0);
}

export function initializeKernel(config, kernelStorage, verbose = false) {
export async function initializeKernel(config, kernelStorage, options = {}) {
const { verbose = false } = options;
const logStartup = verbose ? console.debug : () => 0;
insistStorageAPI(kernelStorage.kvStore);

Expand Down Expand Up @@ -156,6 +157,10 @@ export function initializeKernel(config, kernelStorage, verbose = false) {

// ----------------------------------------------------------------------

/**
* @param {import('../types-internal.js').VatID} bootstrapVatID
* @returns {string} the KPID of the bootstrap message result promise
*/
function enqueueBootstrap(bootstrapVatID) {
// we invoke obj[0].bootstrap with an object that contains 'vats'.
insistVatID(bootstrapVatID);
Expand Down Expand Up @@ -212,6 +217,7 @@ export function initializeKernel(config, kernelStorage, verbose = false) {
const args = kunser(m.serialize(harden([vatObj0s, deviceObj0s])));
const rootKref = exportRootObject(kernelKeeper, bootstrapVatID);
const resultKpid = queueToKref(rootKref, 'bootstrap', args, 'panic');
assert(resultKpid); // appease tsc: 'panic' ensures a kpid is returned
kernelKeeper.incrementRefCount(resultKpid, 'external');
return resultKpid;
}
Expand Down
6 changes: 5 additions & 1 deletion packages/SwingSet/src/controller/initializeSwingset.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ function sortObjectProperties(obj, firsts = []) {
* @param {SwingStoreKernelStorage} kernelStorage
* @param {InitializationOptions} initializationOptions
* @param {{ env?: Record<string, string | undefined > }} runtimeOptions
* @returns {Promise<string | undefined>} KPID of the bootstrap message result promise
*/
export async function initializeSwingset(
config,
Expand Down Expand Up @@ -573,5 +574,8 @@ export async function initializeSwingset(
if (verbose) {
kdebugEnable(true);
}
return initializeKernel(kconfig, kernelStorage);

// returns the kpid of the bootstrap() result
const bootKpid = await initializeKernel(kconfig, kernelStorage);
return bootKpid;
warner marked this conversation as resolved.
Show resolved Hide resolved
}
95 changes: 95 additions & 0 deletions packages/SwingSet/src/controller/startXSnap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import fs from 'fs';
import path from 'path';
import { Fail } from '@agoric/assert';
import { type as osType } from 'os';
import { xsnap, recordXSnap } from '@agoric/xsnap';

const NETSTRING_MAX_CHUNK_SIZE = 12_000_000;

/**
* @param {{ moduleFormat: string, source: string }[]} bundles
* @param {{
* snapStore?: SnapStore,
* spawn: typeof import('child_process').spawn
* debug?: boolean,
* workerTraceRootPath?: string,
* }} options
*/
export function makeStartXSnap(bundles, options) {
// our job is to simply curry some authorities and settings into the
// 'startXSnap' function we return
const { snapStore, spawn, debug = false, workerTraceRootPath } = options;

let doXSnap = xsnap;

if (workerTraceRootPath) {
let serial = 0;
const makeNextTraceDir = () => {
const rel = `${workerTraceRootPath}/${serial}`;
const workerTrace = path.resolve(rel) + path.sep;
serial += 1;
return workerTrace;
};
doXSnap = opts => {
const workerTraceDir = makeNextTraceDir();
console.log('SwingSet xs-worker tracing:', { workerTraceDir });
fs.mkdirSync(workerTraceDir, { recursive: true });
return recordXSnap(opts, workerTraceDir, {
writeFileSync: fs.writeFileSync,
});
};
}

/** @type { import('@agoric/xsnap/src/xsnap').XSnapOptions } */
const xsnapOpts = {
os: osType(),
spawn,
stdout: 'inherit',
stderr: 'inherit',
debug,
netstringMaxChunkSize: NETSTRING_MAX_CHUNK_SIZE,
};

/**
* @param {string} vatID
* @param {string} name
* @param {(request: Uint8Array) => Promise<Uint8Array>} handleCommand
* @param {boolean} [metered]
* @param {boolean} [reload]
*/
async function startXSnap(
vatID,
name,
handleCommand,
metered,
reload = false,
) {
const meterOpts = metered ? {} : { meteringLimit: 0 };
if (snapStore && reload) {
// console.log('startXSnap from', { snapshotHash });
return snapStore.loadSnapshot(vatID, async snapshot => {
const xs = doXSnap({
snapshot,
name,
handleCommand,
...meterOpts,
...xsnapOpts,
});
await xs.isReady();
return xs;
});
}
// console.log('fresh xsnap', { snapStore: snapStore });
const worker = doXSnap({ handleCommand, name, ...meterOpts, ...xsnapOpts });

for (const bundle of bundles) {
bundle.moduleFormat === 'getExport' ||
bundle.moduleFormat === 'nestedEvaluate' ||
Fail`unexpected: ${bundle.moduleFormat}`;
// eslint-disable-next-line no-await-in-loop, @jessie.js/no-nested-await
await worker.evaluate(`(${bundle.source}\n)()`.trim());
}
return worker;
}
return startXSnap;
}
2 changes: 1 addition & 1 deletion packages/SwingSet/test/bundling/test-bundles-kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { initializeKernel } from '../../src/controller/initializeKernel.js';
test('install bundle', async t => {
const endowments = makeKernelEndowments();
const { kvStore } = endowments.kernelStorage;
initializeKernel({}, endowments.kernelStorage);
await initializeKernel({}, endowments.kernelStorage);
const kernel = buildKernel(endowments, {}, {});
await kernel.start(); // empty queue

Expand Down
6 changes: 3 additions & 3 deletions packages/SwingSet/test/test-abandon-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { extractMethod } from '../src/lib/kdebug.js';
import { makeKernelEndowments, buildDispatch } from './util.js';
import { kser, kunser, kslot } from '../src/lib/kmarshal.js';

const makeKernel = () => {
const makeKernel = async () => {
const endowments = makeKernelEndowments();
const { kvStore } = endowments.kernelStorage;
initializeKernel({}, endowments.kernelStorage);
await initializeKernel({}, endowments.kernelStorage);
const kernel = buildKernel(endowments, {}, {});
return { kernel, kvStore };
};
Expand Down Expand Up @@ -77,7 +77,7 @@ async function doAbandon(t, reachable) {
// vatB abandons it
// vatA should retain the object
// sending to the abandoned object should get an error
const { kernel, kvStore } = makeKernel();
const { kernel, kvStore } = await makeKernel();
await kernel.start();

const {
Expand Down
8 changes: 4 additions & 4 deletions packages/SwingSet/test/test-gc-kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ function makeEndowments() {
};
}

function makeKernel() {
async function makeKernel() {
const endowments = makeEndowments();
const { kvStore } = endowments.kernelStorage;
initializeKernel({}, endowments.kernelStorage);
await initializeKernel({}, endowments.kernelStorage);
const kernel = buildKernel(endowments, {}, {});
return { kernel, kvStore };
}
Expand Down Expand Up @@ -110,7 +110,7 @@ async function prep(t, options = {}) {
sendToBob = true,
sendPromiseToCarol = true,
} = options;
const { kernel, kvStore } = makeKernel();
const { kernel, kvStore } = await makeKernel();
await kernel.start();

const vrefs = {}; // track vrefs within vats
Expand Down Expand Up @@ -549,7 +549,7 @@ test('mode24B', async t => {
});

test('retire before drop is error', async t => {
const { kernel } = makeKernel();
const { kernel } = await makeKernel();
await kernel.start();

const amyForAlice = 'o+101';
Expand Down
Loading