Skip to content

Commit

Permalink
overhaul vat-manager and transcript code
Browse files Browse the repository at this point in the history
* remove vatSyscallHandler from manager-factory
  * it becomes an argument to manager.deliver()
  * created by vat-warehouse, not vat-loader
* remove compareSyscalls from manager-factory
  * vat-warehouse embeds it in the syscall handler
* remove transcript.js, functionality merged into vat-warehouse
* remove workerCanBlock: always assumed
* remove useTranscript from manager
  * all deliveries build a transcript entry
  * vat-warehouse only saves it if options.useTranscript is true
* build full anachrophobia log message after delivery is complete
  * show full syscall list for the delivery
  * each annotated as ok/wrong/extra/missing
* move transcript replay into vat-warehouse
* shorter transcript property names
  • Loading branch information
warner committed Apr 19, 2023
1 parent 755d213 commit ae4f7bc
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 399 deletions.
3 changes: 2 additions & 1 deletion packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1516,13 +1516,14 @@ export default function buildKernel(
kernelSlog,
makeSourcedConsole,
kernelKeeper,
buildVatSyscallHandler,
overrideVatManagerOptions,
});

vatWarehouse = makeVatWarehouse({
kernelSlog,
kernelKeeper,
vatLoader,
buildVatSyscallHandler,
panic,
warehousePolicy,
});
Expand Down
15 changes: 2 additions & 13 deletions packages/SwingSet/src/kernel/vat-loader/manager-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export function makeVatManagerFactory({
kernelKeeper,
vatEndowments,
gcTools,
kernelSlog,
});

const xsWorkerFactory = makeXsSubprocessFactory({
Expand All @@ -32,6 +31,7 @@ export function makeVatManagerFactory({
'enablePipelining',
'workerOptions',
'setup',
'retainSyscall',
'bundle',
'metered',
'enableDisavow',
Expand All @@ -41,7 +41,6 @@ export function makeVatManagerFactory({
'reapInterval',
'sourcedConsole',
'name',
'compareSyscalls',
]);
const { setup, bundle, enableSetup = false } = managerOptions;
assert(setup || bundle);
Expand All @@ -59,15 +58,13 @@ export function makeVatManagerFactory({
* syscall.
*
* @param {import('../../types-internal.js').VatID} vatID
* @param {import('../vat-warehouse.js').VatSyscallHandler} vatSyscallHandler
* @param {object} options
* @param {import('../../types-internal.js').ManagerOptions} options.managerOptions
* @param {import('@agoric/swingset-liveslots').LiveSlotsOptions} options.liveSlotsOptions
* @returns { Promise<import('../../types-internal.js').VatManager> }
*/
async function vatManagerFactory(
vatID,
vatSyscallHandler,
{ managerOptions, liveSlotsOptions },
) {
validateManagerOptions(managerOptions);
Expand All @@ -79,19 +76,13 @@ export function makeVatManagerFactory({
}
if (enableSetup) {
if (managerOptions.setup) {
return localFactory.createFromSetup(
vatID,
managerOptions.setup,
managerOptions,
vatSyscallHandler,
);
return localFactory.createFromSetup(vatID, managerOptions);
} else {
return localFactory.createFromBundle(
vatID,
managerOptions.bundle,
managerOptions,
liveSlotsOptions,
vatSyscallHandler,
);
}
} else if (type === 'local') {
Expand All @@ -100,7 +91,6 @@ export function makeVatManagerFactory({
managerOptions.bundle,
managerOptions,
liveSlotsOptions,
vatSyscallHandler,
);
}

Expand All @@ -111,7 +101,6 @@ export function makeVatManagerFactory({
managerOptions.bundle,
managerOptions,
liveSlotsOptions,
vatSyscallHandler,
);
}

Expand Down
141 changes: 20 additions & 121 deletions packages/SwingSet/src/kernel/vat-loader/manager-helper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { assert } from '@agoric/assert';
import '../../types-ambient.js';
import { insistVatDeliveryResult } from '../../lib/message.js';
import { makeTranscriptManager } from './transcript.js';
import {
insistVatDeliveryResult,
insistVatSyscallResult,
} from '../../lib/message.js';

/**
* @typedef {import('@agoric/swingset-liveslots').VatDeliveryObject} VatDeliveryObject
Expand Down Expand Up @@ -62,6 +64,7 @@ import { makeTranscriptManager } from './transcript.js';
*/

/**
* TODO: stale
* This generic helper runs on the manager side. It handles transcript
* record/replay, and errors in the manager-specific code.
*
Expand Down Expand Up @@ -104,36 +107,12 @@ import { makeTranscriptManager } from './transcript.js';
* The returned getManager() function will return a VatManager suitable for
* handing to the kernel, which can use it to send deliveries to the vat.
*
* @param {string} vatID
* @param {KernelKeeper} kernelKeeper
* @param {KernelSlog} kernelSlog
* @param {(vso: VatSyscallObject) => VatSyscallResult} vatSyscallHandler
* @param {boolean} workerCanBlock
* @param {import('./transcript.js').CompareSyscalls} [compareSyscalls]
* @param {boolean} [useTranscript]
* @param {boolean} retainSyscall for unit tests: allow syscalls between deliveries
* @returns {ManagerKit}
*/

function makeManagerKit(
vatID,
kernelSlog,
kernelKeeper,
vatSyscallHandler,
workerCanBlock,
compareSyscalls,
useTranscript,
) {
assert(kernelSlog);
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
/** @type {ReturnType<typeof makeTranscriptManager> | undefined} */
let transcriptManager;
if (useTranscript) {
transcriptManager = makeTranscriptManager(
vatKeeper,
vatID,
compareSyscalls,
);
}
function makeManagerKit(retainSyscall = false) {
let syscallHandler; // empty between deliveries

/** @type { (delivery: VatDeliveryObject) => Promise<VatDeliveryResult> } */
let deliverToWorker;
Expand All @@ -149,12 +128,15 @@ function makeManagerKit(
/**
*
* @param {VatDeliveryObject} delivery
* @param {(vso: VatSyscallObject) => VatSyscallResult} vatSyscallHandler
* @returns {Promise<VatDeliveryResult>} // or Error
*/
async function deliver(delivery) {
if (transcriptManager) {
transcriptManager.startDispatch(delivery);
async function deliver(delivery, vatSyscallHandler) {
assert(vatSyscallHandler);
if (!retainSyscall) {
assert(!syscallHandler);
}
syscallHandler = vatSyscallHandler;
// metering faults (or other reasons why the vat should be
// deterministically terminated) are reported with status= ['error',
// err.message, null]. Any non-deterministic error (unexpected worker
Expand All @@ -163,76 +145,12 @@ function makeManagerKit(
/** @type { VatDeliveryResult } */
const status = await deliverToWorker(delivery);
insistVatDeliveryResult(status);
/** @type { import('../../types-external.js').TranscriptDeliveryResults} */
const tdr = { status: status[0] };
// TODO: if the dispatch failed for whatever reason, and we choose to
// destroy the vat, change what we do with the transcript here.
if (transcriptManager) {
transcriptManager.finishDispatch(tdr);
if (!retainSyscall) {
syscallHandler = undefined;
}
return status;
}

async function replayOneDelivery(delivery, expectedSyscalls, deliveryNum) {
assert(transcriptManager, 'delivery replay with no transcript');
transcriptManager.startReplay();
transcriptManager.startReplayDelivery(expectedSyscalls);

// we slog the replay just like the original, but some fields are missing
/** @type {any} */
const newCrankNum = undefined; // TODO think of a way to correlate this
/** @type {any} */
const kd = undefined;
const vd = delivery;
const replay = true;
const finish = kernelSlog.delivery(
vatID,
newCrankNum,
deliveryNum,
kd,
vd,
replay,
);
const status = await deliver(delivery);
finish(status);
transcriptManager.finishReplayDelivery(deliveryNum);
transcriptManager.checkReplayError();
transcriptManager.finishReplay();
return status;
}

/**
* @param {number | undefined} startPos
* @returns {Promise<number?>} number of deliveries, or null if !useTranscript
*/
async function replayTranscript(startPos) {
// console.log('replay from', { vatID, startPos });

if (transcriptManager) {
const total = vatKeeper.vatStats().transcriptCount;
kernelSlog.write({ type: 'start-replay', vatID, deliveries: total });
// TODO glean deliveryNum better, make sure we get the post-snapshot
// transcript starting point right. getTranscript() should probably
// return [deliveryNum, t] pairs. Think about how to provide an
// accurate crankNum, because I'm not sure I want that in transcript.
let deliveryNum = startPos || 0;
for (const t of vatKeeper.getTranscript(startPos)) {
// if (deliveryNum % 100 === 0) {
// console.debug(`replay vatID:${vatID} deliveryNum:${deliveryNum} / ${total}`);
// }
//
// eslint-disable-next-line no-await-in-loop, @jessie.js/no-nested-await
await replayOneDelivery(t.d, t.syscalls, deliveryNum);
deliveryNum += 1;
}
transcriptManager.checkReplayError();
kernelSlog.write({ type: 'finish-replay', vatID });
return deliveryNum;
}

return null;
}

/**
* vatSyscallObject is an array that starts with the syscall name ('send',
* 'subscribe', etc) followed by all the positional arguments of the
Expand All @@ -243,27 +161,10 @@ function makeManagerKit(
* @param {VatSyscallObject} vso
*/
function syscallFromWorker(vso) {
if (transcriptManager && transcriptManager.inReplay()) {
// We're replaying old messages to bring the vat's internal state
// up-to-date. It will make syscalls like a puppy chasing rabbits in
// its sleep. Gently prevent their twitching paws from doing anything.

// but if the puppy deviates one inch from previous twitches, explode
kernelSlog.syscall(vatID, undefined, vso);
const vres = transcriptManager.simulateSyscall(vso);
return vres;
}

const vres = vatSyscallHandler(vso);
// vres is ['error', reason] or ['ok', null] or ['ok', capdata] or ['ok', string]
const [successFlag, data] = vres;
if (successFlag === 'ok' && data && !workerCanBlock) {
console.log(`warning: syscall returns data, but worker cannot get it`);
}
if (transcriptManager) {
transcriptManager.addSyscall(vso, vres);
}
return vres;
const vsr = syscallHandler(vso);
// vsr is ['error', reason] or ['ok', null] or ['ok', capdata] or ['ok', string]
insistVatSyscallResult(vsr);
return vsr;
}

/**
Expand All @@ -274,8 +175,6 @@ function makeManagerKit(
*/
function getManager(shutdown, makeSnapshot) {
return harden({
replayTranscript,
replayOneDelivery,
deliver,
shutdown,
makeSnapshot,
Expand Down
42 changes: 9 additions & 33 deletions packages/SwingSet/src/kernel/vat-loader/manager-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,16 @@ import {
} from '../../supervisors/supervisor-helper.js';

export function makeLocalVatManagerFactory(tools) {
const { allVatPowers, kernelKeeper, vatEndowments, gcTools, kernelSlog } =
tools;
const { allVatPowers, vatEndowments, gcTools } = tools;

const baseVP = {
makeMarshal: allVatPowers.makeMarshal,
};
// testLog is also a vatPower, only for unit tests

function prepare(vatID, vatSyscallHandler, compareSyscalls, useTranscript) {
const mk = makeManagerKit(
vatID,
kernelSlog,
kernelKeeper,
vatSyscallHandler,
true,
compareSyscalls,
useTranscript,
);
function prepare(managerOptions) {
const { retainSyscall = false } = managerOptions;
const mk = makeManagerKit(retainSyscall);

function finish(dispatch) {
assert.typeof(dispatch, 'function');
Expand All @@ -45,16 +37,11 @@ export function makeLocalVatManagerFactory(tools) {
return { syscall, finish };
}

function createFromSetup(vatID, setup, managerOptions, vatSyscallHandler) {
function createFromSetup(vatID, managerOptions) {
const { setup } = managerOptions;
assert.typeof(setup, 'function', 'setup is not an in-realm function');

const { compareSyscalls, useTranscript } = managerOptions;
const { syscall, finish } = prepare(
vatID,
vatSyscallHandler,
compareSyscalls,
useTranscript,
);
const { syscall, finish } = prepare(managerOptions);
const { testLog } = allVatPowers;
const helpers = harden({}); // DEPRECATED, todo remove from setup()
const state = null; // TODO remove from setup()
Expand All @@ -69,22 +56,11 @@ export function makeLocalVatManagerFactory(tools) {
bundle,
managerOptions,
liveSlotsOptions,
vatSyscallHandler,
) {
const {
enableSetup = false,
sourcedConsole,
compareSyscalls,
useTranscript,
} = managerOptions;
const { enableSetup = false, sourcedConsole } = managerOptions;
assert(sourcedConsole, 'vats need managerOptions.sourcedConsole');

const { syscall, finish } = prepare(
vatID,
vatSyscallHandler,
compareSyscalls,
useTranscript,
);
const { syscall, finish } = prepare(managerOptions);

const vatPowers = harden({
...baseVP,
Expand Down
Loading

0 comments on commit ae4f7bc

Please sign in to comment.