@agoric/swingset-vat 0.33.0-u19.2 → 0.33.0-u21.0

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.
@@ -13,6 +13,12 @@ export function makeStartSubprocessWorkerNode(
13
13
  ).pathname;
14
14
  const args = nodeOptions ? [...nodeOptions] : [];
15
15
  if (profileVats.includes(vatID)) {
16
+ // Enable profiling with a 1 microsecond sampling interval,
17
+ // saving results to a file in the working directory
18
+ // with a portable name derived from the vat name
19
+ // and in particular replacing any colons with dashes.
20
+ // cf. ../kernel/vat-loader/manager-subprocess-node.js
21
+ // https://github.com/Agoric/agoric-sdk/blob/18d561f1b95755e58ca691b26938fc249618018d/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-node.js#L32-L34
16
22
  args.push('--cpu-prof');
17
23
  args.push('--cpu-prof-interval');
18
24
  args.push('1');
@@ -285,11 +285,9 @@ export const upgradeSwingset = kernelStorage => {
285
285
 
286
286
  const buggyKPIDs = []; // [kpid, vatID]
287
287
  for (const vatID of allVatIDs) {
288
- const prefix = `${vatID}.c.`;
289
- const len = prefix.length;
290
288
  const ckpPrefix = `${vatID}.c.kp`;
291
- for (const key of enumeratePrefixedKeys(kvStore, ckpPrefix)) {
292
- const kpid = key.slice(len);
289
+ for (const { suffix } of enumeratePrefixedKeys(kvStore, ckpPrefix)) {
290
+ const kpid = `kp${suffix}`;
293
291
  if (isSettled(kpid)) {
294
292
  const n = notifies.get(kpid);
295
293
  if (!n || !n.includes(vatID)) {
@@ -62,3 +62,5 @@ export function buildRootDeviceNode(tools) {
62
62
  },
63
63
  });
64
64
  }
65
+
66
+ /** @typedef {import('../../types-external.js').Device<ReturnType<typeof buildRootDeviceNode>>} BridgeDevice */
@@ -86,6 +86,7 @@ function makeTimerMap(state = undefined) {
86
86
  return copyState(schedule);
87
87
  }
88
88
 
89
+ /** @param {bigint} time */
89
90
  function eventsFor(time) {
90
91
  assert.typeof(time, 'bigint');
91
92
  for (let i = 0; i < schedule.length && schedule[i].time <= time; i += 1) {
@@ -101,17 +102,29 @@ function makeTimerMap(state = undefined) {
101
102
  // There's some question as to whether it's important to invoke the handlers
102
103
  // in the order of their deadlines. If so, we should probably ensure that the
103
104
  // recorded deadlines don't have finer granularity than the turns.
104
- function add(time, handler, repeater = undefined) {
105
+ /**
106
+ *
107
+ * @param {bigint} time
108
+ * @param {Waker} handler
109
+ * @param {number} [index]
110
+ * @returns {bigint}
111
+ */
112
+ function add(time, handler, index = undefined) {
105
113
  assert.typeof(time, 'bigint');
114
+ /** @type {IndexedHandler} */
106
115
  const handlerRecord =
107
- typeof repeater === 'number' ? { handler, index: repeater } : { handler };
116
+ typeof index === 'number' ? { handler, index } : { handler };
108
117
  const { handlers: records } = eventsFor(time);
109
118
  records.push(handlerRecord);
110
119
  schedule.sort((a, b) => Number(a.time - b.time));
111
120
  return time;
112
121
  }
113
122
 
114
- // Remove and return all pairs indexed by numbers up to target
123
+ /**
124
+ * Remove and return all pairs indexed by numbers up to target
125
+ *
126
+ * @param {bigint} target
127
+ */
115
128
  function removeEventsThrough(target) {
116
129
  assert.typeof(target, 'bigint');
117
130
  const returnValues = [];
@@ -132,33 +145,29 @@ function makeTimerMap(state = undefined) {
132
145
  }
133
146
 
134
147
  // We don't expect this to be called often, so we don't optimize for it.
148
+ /**
149
+ *
150
+ * @param {Waker} targetHandler
151
+ * @returns {bigint[]} times that have been removed (may contain duplicates)
152
+ */
135
153
  function remove(targetHandler) {
154
+ /** @type {bigint[]} */
136
155
  const droppedTimes = [];
137
156
  let i = 0;
138
157
  while (i < schedule.length) {
139
158
  const { time, handlers } = schedule[i];
140
- if (handlers.length === 1) {
141
- if (handlers[0].handler === targetHandler) {
142
- schedule.splice(i, 1);
159
+ // Nothing prevents a particular handler from appearing more than once
160
+ for (let j = handlers.length - 1; j >= 0; j -= 1) {
161
+ if (handlers[j].handler === targetHandler) {
162
+ handlers.splice(j, 1);
143
163
  droppedTimes.push(time);
144
- } else {
145
- i += 1;
146
164
  }
165
+ }
166
+ if (handlers.length === 0) {
167
+ // Splice out this element, preserving `i` so we visit any successor.
168
+ schedule.splice(i, 1);
147
169
  } else {
148
- // Nothing prevents a particular handler from appearing more than once
149
- for (const { handler } of handlers) {
150
- // @ts-expect-error xxx Waker vs IndexedHandler
151
- if (handler === targetHandler && handlers.indexOf(handler) !== -1) {
152
- // @ts-expect-error xxx Waker vs IndexedHandler
153
- handlers.splice(handlers.indexOf(handler), 1);
154
- droppedTimes.push(time);
155
- }
156
- }
157
- if (handlers.length === 0) {
158
- schedule.splice(i, 1);
159
- } else {
160
- i += 1;
161
- }
170
+ i += 1;
162
171
  }
163
172
  }
164
173
  return droppedTimes;
@@ -223,6 +232,7 @@ export function buildRootDeviceNode(tools) {
223
232
  // The latest time poll() was called. This might be a block height or it
224
233
  // might be a time from Date.now(). The current time is not reflected back
225
234
  // to the user.
235
+ /** @type {bigint} */
226
236
  let lastPolled = restart ? restart.lastPolled : 0n;
227
237
  let nextRepeater = restart ? restart.nextRepeater : 0;
228
238
 
@@ -276,6 +286,10 @@ export function buildRootDeviceNode(tools) {
276
286
  saveState();
277
287
  return baseTime;
278
288
  },
289
+ /**
290
+ * @param {Waker} handler
291
+ * @returns {bigint[]} times that have been removed (may contain duplicates)
292
+ */
279
293
  removeWakeup(handler) {
280
294
  const times = deadlines.remove(handler);
281
295
  saveState();
@@ -303,6 +317,10 @@ export function buildRootDeviceNode(tools) {
303
317
  saveState();
304
318
  return index;
305
319
  },
320
+ /**
321
+ * @param {number} index
322
+ * @param {Waker} handler
323
+ */
306
324
  schedule(index, handler) {
307
325
  const nextTime = nextScheduleTime(index, repeaters, lastPolled);
308
326
  deadlines.add(nextTime, handler, index);
@@ -311,6 +329,7 @@ export function buildRootDeviceNode(tools) {
311
329
  },
312
330
  });
313
331
  }
332
+ /** @typedef {import('../../types-external.js').Device<ReturnType<typeof buildRootDeviceNode>>} TimerDevice */
314
333
 
315
334
  // exported for testing. Only buildRootDeviceNode is intended for production
316
335
  // use.
@@ -99,6 +99,7 @@ export default function buildKernel(
99
99
  startSubprocessWorkerNode,
100
100
  startXSnap,
101
101
  writeSlogObject,
102
+ slogDuration,
102
103
  WeakRef,
103
104
  FinalizationRegistry,
104
105
  gcAndFinalize,
@@ -111,11 +112,12 @@ export default function buildKernel(
111
112
  overrideVatManagerOptions = {},
112
113
  } = kernelRuntimeOptions;
113
114
  const logStartup = verbose ? console.debug : () => {};
115
+ if (verbose) kdebugEnable(true);
114
116
 
115
117
  const vatAdminRootKref = kernelStorage.kvStore.get('vatAdminRootKref');
116
118
 
117
119
  const kernelSlog = writeSlogObject
118
- ? makeSlogger(slogCallbacks, writeSlogObject)
120
+ ? makeSlogger(slogCallbacks, writeSlogObject, slogDuration)
119
121
  : makeDummySlogger(slogCallbacks, makeConsole('disabled slogger'));
120
122
 
121
123
  const kernelKeeper = makeKernelKeeper(
@@ -559,6 +561,10 @@ export default function buildKernel(
559
561
  */
560
562
  async function processSend(vatID, target, msg) {
561
563
  insistMessage(msg);
564
+ // DEPRECATED: These counts are available as "crank-start"/"crank-finish"
565
+ // slog entries with crankType "delivery" (the latter count filtered on
566
+ // messageType "send") and "deliver"/"deliver-result" slog entries (the
567
+ // latter count filtered on dispatch "message").
562
568
  kernelKeeper.incStat('dispatches');
563
569
  kernelKeeper.incStat('dispatchDeliver');
564
570
 
@@ -589,6 +595,8 @@ export default function buildKernel(
589
595
  const { vatID, kpid } = message;
590
596
  insistVatID(vatID);
591
597
  insistKernelType('promise', kpid);
598
+ // DEPRECATED: This count is available as "crank-start"/"crank-finish"
599
+ // slog entries with crankType "delivery".
592
600
  kernelKeeper.incStat('dispatches');
593
601
  const vatInfo = vatWarehouse.lookup(vatID);
594
602
  if (!vatInfo) {
@@ -598,6 +606,8 @@ export default function buildKernel(
598
606
  const { meterID } = vatInfo;
599
607
 
600
608
  const p = kernelKeeper.getKernelPromise(kpid);
609
+ // DEPRECATED: This count is available as "deliver"/"deliver-result" slog
610
+ // entries with dispatch "notify".
601
611
  kernelKeeper.incStat('dispatchNotify');
602
612
  const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
603
613
  if (p.state === 'unresolved') {
@@ -1342,14 +1352,15 @@ export default function buildKernel(
1342
1352
  * @returns {Promise<PolicyInput>}
1343
1353
  */
1344
1354
  async function processDeliveryMessage(message) {
1355
+ const messageType = message.type;
1345
1356
  kdebug('');
1346
1357
  // prettier-ignore
1347
1358
  kdebug(`processQ crank ${kernelKeeper.getCrankNumber()} ${JSON.stringify(message)}`);
1348
1359
  kdebug(legibilizeMessage(message));
1349
- kernelSlog.write({
1350
- type: 'crank-start',
1360
+ const finish = kernelSlog.startDuration(['crank-start', 'crank-finish'], {
1351
1361
  crankType: 'delivery',
1352
1362
  crankNum: kernelKeeper.getCrankNumber(),
1363
+ messageType,
1353
1364
  message,
1354
1365
  });
1355
1366
  /** @type { PolicyInput } */
@@ -1451,12 +1462,8 @@ export default function buildKernel(
1451
1462
  const crankNum = kernelKeeper.getCrankNumber();
1452
1463
  kernelKeeper.incrementCrankNumber();
1453
1464
  const { crankhash, activityhash } = kernelKeeper.emitCrankHashes();
1454
- // kernelSlog.write({
1455
- // type: 'kernel-stats',
1456
- // stats: kernelKeeper.getStats(),
1457
- // });
1458
- kernelSlog.write({
1459
- type: 'crank-finish',
1465
+ finish({
1466
+ message: undefined,
1460
1467
  crankNum,
1461
1468
  crankhash,
1462
1469
  activityhash,
@@ -1494,14 +1501,15 @@ export default function buildKernel(
1494
1501
  * @returns {Promise<PolicyInput>}
1495
1502
  */
1496
1503
  async function processAcceptanceMessage(message) {
1504
+ const messageType = message.type;
1497
1505
  kdebug('');
1498
1506
  // prettier-ignore
1499
- kdebug(`processAcceptanceQ crank ${kernelKeeper.getCrankNumber()} ${message.type}`);
1507
+ kdebug(`processAcceptanceQ crank ${kernelKeeper.getCrankNumber()} ${messageType}`);
1500
1508
  // kdebug(legibilizeMessage(message));
1501
- kernelSlog.write({
1502
- type: 'crank-start',
1509
+ const finish = kernelSlog.startDuration(['crank-start', 'crank-finish'], {
1503
1510
  crankType: 'routing',
1504
1511
  crankNum: kernelKeeper.getCrankNumber(),
1512
+ messageType,
1505
1513
  message,
1506
1514
  });
1507
1515
  /** @type { PolicyInput } */
@@ -1538,8 +1546,8 @@ export default function buildKernel(
1538
1546
  const crankNum = kernelKeeper.getCrankNumber();
1539
1547
  kernelKeeper.incrementCrankNumber();
1540
1548
  const { crankhash, activityhash } = kernelKeeper.emitCrankHashes();
1541
- kernelSlog.write({
1542
- type: 'crank-finish',
1549
+ finish({
1550
+ message: undefined,
1543
1551
  crankNum,
1544
1552
  crankhash,
1545
1553
  activityhash,
@@ -1938,15 +1946,63 @@ export default function buildKernel(
1938
1946
  }
1939
1947
  }
1940
1948
 
1941
- function reapAllVats() {
1949
+ /** @returns {Generator<VatID>} */
1950
+ function* getAllVatIds() {
1942
1951
  for (const [_, vatID] of kernelKeeper.getStaticVats()) {
1943
- kernelKeeper.scheduleReap(vatID);
1952
+ yield vatID;
1944
1953
  }
1945
1954
  for (const vatID of kernelKeeper.getDynamicVats()) {
1946
- kernelKeeper.scheduleReap(vatID);
1955
+ yield vatID;
1947
1956
  }
1948
1957
  }
1949
1958
 
1959
+ function* getAllVatPosEntries() {
1960
+ for (const vatID of getAllVatIds()) {
1961
+ const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
1962
+ yield /** @type {const} */ ([
1963
+ vatID,
1964
+ vatKeeper.getTranscriptEndPosition(),
1965
+ ]);
1966
+ }
1967
+ }
1968
+
1969
+ function reapAllVats(previousVatPos = {}) {
1970
+ /** @type {Record<string, number>} */
1971
+ const currentVatPos = {};
1972
+
1973
+ for (const [vatID, endPos] of getAllVatPosEntries()) {
1974
+ const vatUsesTranscript = endPos !== 0;
1975
+ if (!vatUsesTranscript) {
1976
+ // The comms vat is a little special. It doesn't use a transcript,
1977
+ // doesn't implement BOYD, and is created with a never reap threshold.
1978
+ //
1979
+ // Here we conflate a vat that doesn't use a transcript with a vat
1980
+ // that cannot reap. We do not bother checking the vat options because
1981
+ // in tests we would actually like to force reap normal vats that may
1982
+ // have been configured with a never threshold.
1983
+ continue;
1984
+ } else if ((previousVatPos[vatID] ?? 0) < endPos) {
1985
+ kernelKeeper.scheduleReap(vatID);
1986
+ // We just added one delivery
1987
+ currentVatPos[vatID] = endPos + 1;
1988
+ } else {
1989
+ currentVatPos[vatID] = endPos;
1990
+ }
1991
+ }
1992
+
1993
+ return harden(currentVatPos);
1994
+ }
1995
+
1996
+ async function snapshotAllVats() {
1997
+ const snapshottedVats = [];
1998
+ await null;
1999
+ for (const vatID of getAllVatIds()) {
2000
+ const snapshotted = await vatWarehouse.maybeSaveSnapshot(vatID, 2);
2001
+ if (snapshotted) snapshottedVats.push(vatID);
2002
+ }
2003
+ return harden(snapshottedVats);
2004
+ }
2005
+
1950
2006
  async function step() {
1951
2007
  if (kernelPanic) {
1952
2008
  throw kernelPanic;
@@ -2151,6 +2207,7 @@ export default function buildKernel(
2151
2207
  run,
2152
2208
  shutdown,
2153
2209
  reapAllVats,
2210
+ snapshotAllVats,
2154
2211
  changeKernelOptions,
2155
2212
 
2156
2213
  // the rest are for testing and debugging
@@ -2161,6 +2218,11 @@ export default function buildKernel(
2161
2218
  ephemeral.log.push(`${str}`);
2162
2219
  },
2163
2220
 
2221
+ /**
2222
+ * Return a fresh stats snapshot.
2223
+ *
2224
+ * @returns {Record<string, number>}
2225
+ */
2164
2226
  getStats() {
2165
2227
  return kernelKeeper.getStats();
2166
2228
  },
@@ -1,9 +1,12 @@
1
- import { q } from '@endo/errors';
1
+ import { q, Fail } from '@endo/errors';
2
+ import { makePromiseKit } from '@endo/promise-kit';
2
3
  import { objectMap } from '@agoric/internal';
4
+ import { defineName } from '@agoric/internal/src/js-utils.js';
3
5
  import { makeLimitedConsole } from '@agoric/internal/src/ses-utils.js';
4
6
 
5
7
  /** @import {Callable} from '@agoric/internal'; */
6
8
  /** @import {LimitedConsole} from '@agoric/internal/src/js-utils.js'; */
9
+ /** @import {SlogProps, SlogDurationProps, SwingsetController} from '../controller/controller.js'; */
7
10
 
8
11
  const IDLE = 'idle';
9
12
  const STARTUP = 'startup';
@@ -12,7 +15,7 @@ const DELIVERY = 'delivery';
12
15
  const noopFinisher = harden(() => {});
13
16
 
14
17
  /** @typedef {(...finishArgs: unknown[]) => unknown} AnyFinisher */
15
- /** @typedef {Partial<Record<Exclude<keyof KernelSlog, 'write'>, (methodName: string, args: unknown[], finisher: AnyFinisher) => unknown>>} SlogWrappers */
18
+ /** @typedef {Partial<Record<Exclude<keyof KernelSlog, 'write' | 'startDuration'>, (methodName: string, args: unknown[], finisher: AnyFinisher) => unknown>>} SlogWrappers */
16
19
 
17
20
  /**
18
21
  * Support asynchronous slog callbacks that are invoked at the start
@@ -96,24 +99,81 @@ export function makeDummySlogger(slogCallbacks, dummyConsole = badConsole) {
96
99
  changeCList: () => noopFinisher,
97
100
  terminateVat: () => noopFinisher,
98
101
  });
99
- return harden({ ...wrappedMethods, write: noopFinisher });
102
+ return harden({
103
+ ...wrappedMethods,
104
+ write: noopFinisher,
105
+ startDuration: () => noopFinisher,
106
+ });
100
107
  }
101
108
 
109
+ /**
110
+ * @callback StartDuration
111
+ * Capture an extended process, writing an entry with `type` $startLabel and
112
+ * then later (when the returned finish function is called) another entry with
113
+ * `type` $endLabel and `seconds` reporting the intervening duration.
114
+ *
115
+ * @param {readonly [startLabel: string, endLabel: string]} labels
116
+ * @param {SlogDurationProps} startProps
117
+ * @returns {import('../types-external').FinishSlogDuration}
118
+ */
119
+
102
120
  /**
103
121
  * @param {SlogWrappers} slogCallbacks
104
- * @param {(obj: object) => void} [writeObj]
122
+ * @param {SwingsetController['writeSlogObject']} [writeSlogObject]
123
+ * @param {SwingsetController['slogDuration']} [slogDuration] required when writeSlogObject is provided
105
124
  * @returns {KernelSlog}
106
125
  */
107
- export function makeSlogger(slogCallbacks, writeObj) {
108
- const safeWrite = writeObj
109
- ? obj => {
110
- try {
111
- writeObj(obj);
112
- } catch (err) {
113
- console.error('WARNING: slogger write error', err);
126
+ export function makeSlogger(slogCallbacks, writeSlogObject, slogDuration) {
127
+ if (writeSlogObject && !slogDuration) {
128
+ throw Fail`slogDuration is required with writeSlogObject`;
129
+ }
130
+ const { safeWrite, startDuration } = (() => {
131
+ if (!writeSlogObject) {
132
+ const dummySafeWrite = () => {};
133
+ /** @type {StartDuration} */
134
+ const dummyStartDuration = () => defineName('dummyFinish', () => {});
135
+ return { safeWrite: dummySafeWrite, startDuration: dummyStartDuration };
136
+ }
137
+ /** @type {(obj: SlogProps) => void} */
138
+ // eslint-disable-next-line no-shadow
139
+ const safeWrite = obj => {
140
+ try {
141
+ writeSlogObject(obj);
142
+ } catch (err) {
143
+ console.error('WARNING: slogger write error', err);
144
+ }
145
+ };
146
+ /** @type {StartDuration} */
147
+ // eslint-disable-next-line no-shadow
148
+ const startDuration = (labels, startProps) => {
149
+ try {
150
+ /** @type {(extraProps?: SlogDurationProps) => void} */
151
+ let closeSpan;
152
+ // @ts-expect-error TS2722 slogDuration is not undefined here
153
+ void slogDuration(labels, startProps, async finish => {
154
+ const doneKit = makePromiseKit();
155
+ closeSpan = props => {
156
+ try {
157
+ finish(props);
158
+ } finally {
159
+ doneKit.resolve(undefined);
160
+ }
161
+ };
162
+ // Hold the span open until `finish` is called.
163
+ await doneKit.promise;
164
+ });
165
+ // @ts-expect-error TS2454 closeSpan must have been assigned above
166
+ if (typeof closeSpan !== 'function') {
167
+ throw Fail`slogDuration did not synchronously provide a finisher`;
114
168
  }
169
+ return closeSpan;
170
+ } catch (err) {
171
+ console.error('WARNING: slogger write error', err);
172
+ return defineName('dummyFinish', () => {});
115
173
  }
116
- : () => {};
174
+ };
175
+ return { safeWrite, startDuration };
176
+ })();
117
177
 
118
178
  const vatSlogs = new Map(); // vatID -> vatSlog
119
179
 
@@ -149,15 +209,17 @@ export function makeSlogger(slogCallbacks, writeObj) {
149
209
 
150
210
  function startup() {
151
211
  // provide a context for console calls during startup
152
- checkOldState(IDLE, 'did startup get called twice?');
212
+ checkOldState(IDLE, 'vat-startup called twice?');
153
213
  state = STARTUP;
154
- safeWrite({ type: 'vat-startup-start', vatID });
155
- function finish() {
156
- checkOldState(STARTUP, 'startup-finish called twice?');
214
+ const finish = startDuration(
215
+ ['vat-startup-start', 'vat-startup-finish'],
216
+ { vatID },
217
+ );
218
+ return harden(() => {
219
+ checkOldState(STARTUP, 'vat-startup-finish called twice?');
157
220
  state = IDLE;
158
- safeWrite({ type: 'vat-startup-finish', vatID });
159
- }
160
- return harden(finish);
221
+ finish();
222
+ });
161
223
  }
162
224
 
163
225
  // kd: kernelDelivery, vd: vatDelivery
@@ -167,32 +229,44 @@ export function makeSlogger(slogCallbacks, writeObj) {
167
229
  crankNum = newCrankNum;
168
230
  deliveryNum = newDeliveryNum;
169
231
  replay = inReplay;
170
- const when = { crankNum, vatID, deliveryNum, replay };
171
- safeWrite({ type: 'deliver', ...when, kd, vd });
172
232
  syscallNum = 0;
173
-
233
+ const dispatch = vd?.[0]; // using vd because kd is undefined in replay
234
+ const finish = startDuration(['deliver', 'deliver-result'], {
235
+ crankNum,
236
+ vatID,
237
+ deliveryNum,
238
+ replay,
239
+ dispatch,
240
+ kd,
241
+ vd,
242
+ });
174
243
  // dr: deliveryResult
175
- function finish(dr) {
176
- checkOldState(DELIVERY, 'delivery-finish called twice?');
177
- safeWrite({ type: 'deliver-result', ...when, dr });
244
+ return harden(dr => {
245
+ checkOldState(DELIVERY, 'deliver-result called twice?');
178
246
  state = IDLE;
179
- }
180
- return harden(finish);
247
+ finish({ kd: undefined, vd: undefined, dr });
248
+ });
181
249
  }
182
250
 
183
251
  // ksc: kernelSyscallObject, vsc: vatSyscallObject
184
252
  function syscall(ksc, vsc) {
185
253
  checkOldState(DELIVERY, 'syscall invoked outside of delivery');
186
- const when = { crankNum, vatID, deliveryNum, syscallNum, replay };
187
- safeWrite({ type: 'syscall', ...when, ksc, vsc });
254
+ const finish = startDuration(['syscall', 'syscall-result'], {
255
+ crankNum,
256
+ vatID,
257
+ deliveryNum,
258
+ syscallNum,
259
+ replay,
260
+ syscall: vsc?.[0],
261
+ ksc,
262
+ vsc,
263
+ });
188
264
  syscallNum += 1;
189
-
190
265
  // ksr: kernelSyscallResult, vsr: vatSyscallResult
191
- function finish(ksr, vsr) {
266
+ return harden((ksr, vsr) => {
192
267
  checkOldState(DELIVERY, 'syscall finished after delivery?');
193
- safeWrite({ type: 'syscall-result', ...when, ksr, vsr });
194
- }
195
- return harden(finish);
268
+ finish({ ksc: undefined, vsc: undefined, ksr, vsr });
269
+ });
196
270
  }
197
271
 
198
272
  // mode: 'import' | 'export' | 'drop'
@@ -258,5 +332,5 @@ export function makeSlogger(slogCallbacks, writeObj) {
258
332
  terminateVat: (vatID, ...args) =>
259
333
  provideVatSlogger(vatID).vatSlog.terminateVat(...args),
260
334
  });
261
- return harden({ ...wrappedMethods, write: safeWrite });
335
+ return harden({ ...wrappedMethods, write: safeWrite, startDuration });
262
336
  }
@@ -189,11 +189,12 @@ export function makeDeviceKeeper(kvStore, deviceID, tools) {
189
189
  /** @type {Array<[string, string, string]>} */
190
190
  const res = [];
191
191
  const prefix = `${deviceID}.c.`;
192
- for (const k of enumeratePrefixedKeys(kvStore, prefix)) {
193
- const slot = k.slice(prefix.length);
192
+ const prefixedKeys = enumeratePrefixedKeys(kvStore, prefix);
193
+
194
+ for (const { key, suffix: slot } of prefixedKeys) {
194
195
  if (!slot.startsWith('k')) {
195
196
  const devSlot = slot;
196
- const kernelSlot = kvStore.get(k);
197
+ const kernelSlot = kvStore.get(key);
197
198
  res.push([kernelSlot, deviceID, devSlot]);
198
199
  }
199
200
  }