@agoric/swingset-vat 0.33.0-u18a.0 → 0.33.0-u19.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.
@@ -1,120 +1,127 @@
1
1
  import { q } from '@endo/errors';
2
+ import { makeLimitedConsole } from '@agoric/internal/src/ses-utils.js';
3
+
4
+ /** @import {LimitedConsole} from '@agoric/internal/src/js-utils.js'; */
2
5
 
3
6
  const IDLE = 'idle';
4
7
  const STARTUP = 'startup';
5
8
  const DELIVERY = 'delivery';
6
9
 
7
- function makeCallbackRegistry(callbacks) {
8
- const todo = new Set(Object.keys(callbacks));
10
+ const noopFinisher = harden(() => {});
11
+
12
+ /** @typedef {(...finishArgs: unknown[]) => unknown} AnyFinisher */
13
+ /** @typedef {Partial<Record<Exclude<keyof KernelSlog, 'write'>, (methodName: string, args: unknown[], finisher: AnyFinisher) => unknown>>} SlogWrappers */
14
+
15
+ /**
16
+ * Support composition of asynchronous callbacks that are invoked at the start
17
+ * of an operation and return either a non-function result or a "finisher"
18
+ * function to be invoked upon operation completion.
19
+ * This maker accepts a collection of wrapper functions that receive the same
20
+ * arguments as the method they wrap, along with the result of that method
21
+ * (e.g., its finisher), and are expected to return a finisher of their own that
22
+ * will invoke that wrapped finisher.
23
+ *
24
+ * @param {SlogWrappers} wrappers
25
+ */
26
+ function makeFinishersKit(wrappers) {
27
+ const unused = new Set(Object.keys(wrappers));
9
28
  return harden({
10
29
  /**
11
- * Robustly wrap a method with a callbacks[method] function, if defined. We
12
- * incur no runtime overhead if the given callback method isn't defined.
30
+ * Robustly wrap a method if a wrapper is defined.
13
31
  *
14
- * @param {string} method wrap with callbacks[method]
15
- * @param {(...args: Array<unknown>) => unknown} impl the original
16
- * implementation of the method
17
- * @returns {(...args: Array<unknown>) => unknown} the wrapped method if the
18
- * callback is defined, or original method if not
32
+ * @template {(...args: unknown[]) => (Finisher | unknown)} F
33
+ * @template {AnyFinisher} [Finisher=AnyFinisher]
34
+ * @param {string} method name
35
+ * @param {F} impl the original implementation
36
+ * @returns {F} the wrapped method
19
37
  */
20
- registerCallback(method, impl) {
21
- todo.delete(method);
22
- const cb = callbacks[method];
23
- if (!cb) {
24
- // No registered callback, just use the implementation directly.
25
- // console.error('no registered callback for', method);
26
- return impl;
27
- }
38
+ wrap(method, impl) {
39
+ unused.delete(method);
40
+ const wrapper = wrappers[method];
41
+
42
+ // If there is no registered wrapper, return the implementation directly.
43
+ if (!wrapper) return impl;
28
44
 
29
- return (...args) => {
30
- // Invoke the implementation first.
31
- const ret = impl(...args);
45
+ const wrapped = (...args) => {
46
+ const maybeFinisher = /** @type {Finisher} */ (impl(...args));
32
47
  try {
33
- // Allow the callback to observe the call synchronously, and affect
34
- // the finisher function, but not to throw an exception.
35
- const cbRet = cb(method, args, ret);
36
- if (typeof ret === 'function') {
37
- // We wrap the finisher in the callback's return value.
38
- return (...finishArgs) => {
39
- try {
40
- return cbRet(...finishArgs);
41
- } catch (e) {
42
- console.error(
43
- `failed to call registered ${method}.finish function:`,
44
- e,
45
- );
46
- }
47
- return ret(...args);
48
- };
49
- }
50
- // We just return the callback's return value.
51
- return cbRet;
48
+ // Allow the callback to observe the call synchronously, and replace
49
+ // the implementation's finisher function, but not to throw an exception.
50
+ const wrapperFinisher = wrapper(method, args, maybeFinisher);
51
+ if (typeof maybeFinisher !== 'function') return wrapperFinisher;
52
+
53
+ // We wrap the finisher in the callback's return value.
54
+ return (...finishArgs) => {
55
+ try {
56
+ return /** @type {Finisher} */ (wrapperFinisher)(...finishArgs);
57
+ } catch (e) {
58
+ console.error(`${method} wrapper finisher failed:`, e);
59
+ return maybeFinisher(...finishArgs);
60
+ }
61
+ };
52
62
  } catch (e) {
53
- console.error('failed to call registered', method, 'callback:', e);
63
+ console.error(`${method} wrapper failed:`, e);
64
+ return maybeFinisher;
54
65
  }
55
- return ret;
56
66
  };
67
+ return /** @type {F} */ (wrapped);
57
68
  },
58
69
  /**
59
- * Declare that all the methods have been registered.
70
+ * Declare that all wrapping is done.
60
71
  *
61
- * @param {string} errorUnusedMsg message to display if there are callback
62
- * names that don't correspond to a registration
72
+ * @param {string} msg message to display if there are unused wrappers
63
73
  */
64
- doneRegistering(errorUnusedMsg = `Unrecognized callback names:`) {
65
- const cbNames = [...todo.keys()];
66
- if (!cbNames.length) {
67
- return;
68
- }
69
- console.warn(errorUnusedMsg, cbNames.map(q).sort().join(', '));
74
+ done(msg = 'Unused wrappers') {
75
+ if (!unused.size) return;
76
+ console.warn(msg, ...[...unused.keys()].sort().map(q));
70
77
  },
71
78
  });
72
79
  }
73
80
 
81
+ export const badConsole = makeLimitedConsole(level => () => {
82
+ throw Error(`unexpected use of badConsole.${level}`);
83
+ });
84
+ export const noopConsole = makeLimitedConsole(_level => () => {});
85
+
74
86
  /**
75
- * @param {*} slogCallbacks
76
- * @param {Pick<Console, 'debug'|'log'|'info'|'warn'|'error'>} dummyConsole
87
+ * @param {SlogWrappers} slogCallbacks
88
+ * @param {LimitedConsole} [dummyConsole]
77
89
  * @returns {KernelSlog}
78
90
  */
79
- export function makeDummySlogger(slogCallbacks, dummyConsole) {
80
- const { registerCallback: reg, doneRegistering } =
81
- makeCallbackRegistry(slogCallbacks);
91
+ export function makeDummySlogger(slogCallbacks, dummyConsole = badConsole) {
92
+ const { wrap, done } = makeFinishersKit(slogCallbacks);
82
93
  const dummySlogger = harden({
83
- provideVatSlogger: reg('provideVatSlogger', () =>
84
- harden({
85
- vatSlog: {
86
- delivery: () => () => 0,
87
- },
88
- }),
89
- ),
90
- vatConsole: reg('vatConsole', () => dummyConsole),
91
- startup: reg('startup', () => () => 0), // returns nop finish() function
92
- replayVatTranscript: reg('replayVatTranscript', () => () => 0),
93
- delivery: reg('delivery', () => () => 0),
94
- syscall: reg('syscall', () => () => 0),
95
- changeCList: reg('changeCList', () => () => 0),
96
- terminateVat: reg('terminateVat', () => () => 0),
97
- write: () => 0,
94
+ provideVatSlogger: wrap('provideVatSlogger', () => {
95
+ return harden({ vatSlog: { delivery: () => noopFinisher } });
96
+ }),
97
+ vatConsole: wrap('vatConsole', () => dummyConsole),
98
+ startup: wrap('startup', () => noopFinisher),
99
+ replayVatTranscript: wrap('replayVatTranscript', () => noopFinisher),
100
+ delivery: wrap('delivery', () => noopFinisher),
101
+ syscall: wrap('syscall', () => noopFinisher),
102
+ changeCList: wrap('changeCList', () => noopFinisher),
103
+ terminateVat: wrap('terminateVat', () => noopFinisher),
104
+ write: noopFinisher,
98
105
  });
99
- doneRegistering(`Unrecognized makeDummySlogger slogCallbacks names:`);
100
- // @ts-expect-error xxx
106
+ done('Unused makeDummySlogger slogCallbacks method names');
101
107
  return dummySlogger;
102
108
  }
103
109
 
104
110
  /**
105
- * @param {*} slogCallbacks
106
- * @param {*} writeObj
111
+ * @param {SlogWrappers} slogCallbacks
112
+ * @param {(obj: object) => void} [writeObj]
107
113
  * @returns {KernelSlog}
108
114
  */
109
115
  export function makeSlogger(slogCallbacks, writeObj) {
110
- const safeWrite = e => {
111
- try {
112
- writeObj(e);
113
- } catch (err) {
114
- console.error('WARNING: slogger write error', err);
115
- }
116
- };
117
- const write = writeObj ? safeWrite : () => 0;
116
+ const safeWrite = writeObj
117
+ ? obj => {
118
+ try {
119
+ writeObj(obj);
120
+ } catch (err) {
121
+ console.error('WARNING: slogger write error', err);
122
+ }
123
+ }
124
+ : () => {};
118
125
 
119
126
  const vatSlogs = new Map(); // vatID -> vatSlog
120
127
 
@@ -130,36 +137,33 @@ export function makeSlogger(slogCallbacks, writeObj) {
130
137
  console.error(
131
138
  `WARNING: slogger state confused: vat ${vatID} in ${state}, not ${exp}: ${msg}`,
132
139
  );
133
- write({ type: 'slogger-confused', vatID, state, exp, msg });
140
+ safeWrite({ type: 'slogger-confused', vatID, state, exp, msg });
134
141
  }
135
142
  }
136
143
 
137
144
  function vatConsole(sourcedConsole) {
138
- const vc = {};
139
- for (const level of ['debug', 'log', 'info', 'warn', 'error']) {
140
- vc[level] = (sourceTag, ...args) => {
141
- if (replay) {
142
- // Don't duplicate stale console output.
143
- return;
144
- }
145
- sourcedConsole[level](sourceTag, ...args);
146
- const when = { state, crankNum, vatID, deliveryNum };
147
- const source = sourceTag === 'ls' ? 'liveslots' : sourceTag;
148
- write({ type: 'console', source, ...when, level, args });
149
- };
150
- }
151
- return harden(vc);
145
+ return makeLimitedConsole(level => (source, ...args) => {
146
+ // Don't duplicate stale output.
147
+ if (replay) return;
148
+
149
+ // Write to the console, then to the slog.
150
+ sourcedConsole[level](source, ...args);
151
+ // TODO: Just use "liveslots" rather than "ls"?
152
+ if (source === 'ls') source = 'liveslots';
153
+ const when = { state, crankNum, vatID, deliveryNum };
154
+ safeWrite({ type: 'console', source, ...when, level, args });
155
+ });
152
156
  }
153
157
 
154
158
  function startup() {
155
159
  // provide a context for console calls during startup
156
160
  checkOldState(IDLE, 'did startup get called twice?');
157
161
  state = STARTUP;
158
- write({ type: 'vat-startup-start', vatID });
162
+ safeWrite({ type: 'vat-startup-start', vatID });
159
163
  function finish() {
160
164
  checkOldState(STARTUP, 'startup-finish called twice?');
161
165
  state = IDLE;
162
- write({ type: 'vat-startup-finish', vatID });
166
+ safeWrite({ type: 'vat-startup-finish', vatID });
163
167
  }
164
168
  return harden(finish);
165
169
  }
@@ -172,13 +176,13 @@ export function makeSlogger(slogCallbacks, writeObj) {
172
176
  deliveryNum = newDeliveryNum;
173
177
  replay = inReplay;
174
178
  const when = { crankNum, vatID, deliveryNum, replay };
175
- write({ type: 'deliver', ...when, kd, vd });
179
+ safeWrite({ type: 'deliver', ...when, kd, vd });
176
180
  syscallNum = 0;
177
181
 
178
182
  // dr: deliveryResult
179
183
  function finish(dr) {
180
184
  checkOldState(DELIVERY, 'delivery-finish called twice?');
181
- write({ type: 'deliver-result', ...when, dr });
185
+ safeWrite({ type: 'deliver-result', ...when, dr });
182
186
  state = IDLE;
183
187
  }
184
188
  return harden(finish);
@@ -188,24 +192,24 @@ export function makeSlogger(slogCallbacks, writeObj) {
188
192
  function syscall(ksc, vsc) {
189
193
  checkOldState(DELIVERY, 'syscall invoked outside of delivery');
190
194
  const when = { crankNum, vatID, deliveryNum, syscallNum, replay };
191
- write({ type: 'syscall', ...when, ksc, vsc });
195
+ safeWrite({ type: 'syscall', ...when, ksc, vsc });
192
196
  syscallNum += 1;
193
197
 
194
198
  // ksr: kernelSyscallResult, vsr: vatSyscallResult
195
199
  function finish(ksr, vsr) {
196
200
  checkOldState(DELIVERY, 'syscall finished after delivery?');
197
- write({ type: 'syscall-result', ...when, ksr, vsr });
201
+ safeWrite({ type: 'syscall-result', ...when, ksr, vsr });
198
202
  }
199
203
  return harden(finish);
200
204
  }
201
205
 
202
206
  // mode: 'import' | 'export' | 'drop'
203
207
  function changeCList(crank, mode, kobj, vobj) {
204
- write({ type: 'clist', crankNum: crank, mode, vatID, kobj, vobj });
208
+ safeWrite({ type: 'clist', crankNum: crank, mode, vatID, kobj, vobj });
205
209
  }
206
210
 
207
211
  function terminateVat(shouldReject, info) {
208
- write({ type: 'terminate', vatID, shouldReject, info });
212
+ safeWrite({ type: 'terminate', vatID, shouldReject, info });
209
213
  }
210
214
 
211
215
  return harden({
@@ -233,7 +237,7 @@ export function makeSlogger(slogCallbacks, writeObj) {
233
237
  }
234
238
  const vatSlog = makeVatSlog(vatID);
235
239
  vatSlogs.set(vatID, vatSlog);
236
- write({
240
+ safeWrite({
237
241
  type: 'create-vat',
238
242
  vatID,
239
243
  dynamic,
@@ -247,43 +251,42 @@ export function makeSlogger(slogCallbacks, writeObj) {
247
251
  }
248
252
 
249
253
  function replayVatTranscript(vatID) {
250
- write({ type: 'replay-transcript-start', vatID });
254
+ safeWrite({ type: 'replay-transcript-start', vatID });
251
255
  function finish() {
252
- write({ type: 'replay-transcript-finish', vatID });
256
+ safeWrite({ type: 'replay-transcript-finish', vatID });
253
257
  }
254
258
  return harden(finish);
255
259
  }
256
260
 
257
261
  // function annotateVat(vatID, data) {
258
- // write({ type: 'annotate-vat', vatID, data });
262
+ // safeWrite({ type: 'annotate-vat', vatID, data });
259
263
  // }
260
264
 
261
- const { registerCallback: reg, doneRegistering } =
262
- makeCallbackRegistry(slogCallbacks);
265
+ const { wrap, done } = makeFinishersKit(slogCallbacks);
263
266
  const slogger = harden({
264
- provideVatSlogger: reg('provideVatSlogger', provideVatSlogger),
265
- vatConsole: reg('vatConsole', (vatID, ...args) =>
267
+ provideVatSlogger: wrap('provideVatSlogger', provideVatSlogger),
268
+ vatConsole: wrap('vatConsole', (vatID, ...args) =>
266
269
  provideVatSlogger(vatID).vatSlog.vatConsole(...args),
267
270
  ),
268
- startup: reg('startup', (vatID, ...args) =>
271
+ startup: wrap('startup', (vatID, ...args) =>
269
272
  provideVatSlogger(vatID).vatSlog.startup(...args),
270
273
  ),
274
+ // TODO: Remove this seemingly dead code.
271
275
  replayVatTranscript,
272
- delivery: reg('delivery', (vatID, ...args) =>
276
+ delivery: wrap('delivery', (vatID, ...args) =>
273
277
  provideVatSlogger(vatID).vatSlog.delivery(...args),
274
278
  ),
275
- syscall: reg('syscall', (vatID, ...args) =>
279
+ syscall: wrap('syscall', (vatID, ...args) =>
276
280
  provideVatSlogger(vatID).vatSlog.syscall(...args),
277
281
  ),
278
- changeCList: reg('changeCList', (vatID, ...args) =>
282
+ changeCList: wrap('changeCList', (vatID, ...args) =>
279
283
  provideVatSlogger(vatID).vatSlog.changeCList(...args),
280
284
  ),
281
- terminateVat: reg('terminateVat', (vatID, ...args) =>
285
+ terminateVat: wrap('terminateVat', (vatID, ...args) =>
282
286
  provideVatSlogger(vatID).vatSlog.terminateVat(...args),
283
287
  ),
284
- write,
288
+ write: safeWrite,
285
289
  });
286
- doneRegistering(`Unrecognized makeSlogger slogCallbacks names:`);
287
- // @ts-expect-error xxx
290
+ done('Unused makeSlogger slogCallbacks method names');
288
291
  return slogger;
289
292
  }
@@ -1,5 +1,7 @@
1
1
  import { Nat, isNat } from '@endo/nat';
2
2
  import { assert, Fail } from '@endo/errors';
3
+ import { naturalCompare } from '@agoric/internal/src/natural-sort.js';
4
+ import { makeDummySlogger, noopConsole } from '../slogger.js';
3
5
  import {
4
6
  initializeVatState,
5
7
  makeVatKeeper,
@@ -329,7 +331,7 @@ export const DEFAULT_GC_KREFS_PER_BOYD = 20;
329
331
  /**
330
332
  * @param {SwingStoreKernelStorage} kernelStorage
331
333
  * @param {number | 'uninitialized'} expectedVersion
332
- * @param {KernelSlog} [kernelSlog]
334
+ * @param {KernelSlog} [kernelSlog] optional only for expectedVersion 'uninitialized'
333
335
  */
334
336
  export default function makeKernelKeeper(
335
337
  kernelStorage,
@@ -355,10 +357,13 @@ export default function makeKernelKeeper(
355
357
  if (versionString) {
356
358
  throw Error(`kernel DB already initialized (v${versionString})`);
357
359
  }
360
+ kernelSlog ||= makeDummySlogger({}, noopConsole);
358
361
  } else if (expectedVersion !== version) {
359
362
  throw Error(
360
363
  `kernel DB is too old: has version v${version}, but expected v${expectedVersion}`,
361
364
  );
365
+ } else if (!kernelSlog) {
366
+ throw Error('kernelSlog is required for an already-initialized kernel DB');
362
367
  } else {
363
368
  // DB is up-to-date, so populate any caches we use
364
369
  terminatedVats = JSON.parse(getRequired('vats.terminated'));
@@ -1886,39 +1891,12 @@ export default function makeKernelKeeper(
1886
1891
  }
1887
1892
  }
1888
1893
 
1889
- function compareNumbers(a, b) {
1890
- return Number(a - b);
1891
- }
1892
-
1893
- function compareStrings(a, b) {
1894
- // natural-sort strings having a shared prefix followed by digits
1895
- // (e.g., 'ko42' and 'ko100')
1896
- const [_a, aPrefix, aDigits] = /^(\D+)(\d+)$/.exec(a) || [];
1897
- if (aPrefix) {
1898
- const [_b, bPrefix, bDigits] = /^(\D+)(\d+)$/.exec(b) || [];
1899
- if (bPrefix === aPrefix) {
1900
- return compareNumbers(aDigits, bDigits);
1901
- }
1902
- }
1903
-
1904
- // otherwise use the default string ordering
1905
- if (a > b) {
1906
- return 1;
1907
- }
1908
- if (a < b) {
1909
- return -1;
1910
- }
1911
- return 0;
1912
- }
1913
-
1894
+ // Perform an element-by-element natural sort.
1914
1895
  kernelTable.sort(
1915
1896
  (a, b) =>
1916
- compareStrings(a[0], b[0]) ||
1917
- compareStrings(a[1], b[1]) ||
1918
- compareNumbers(a[2], b[2]) ||
1919
- compareStrings(a[3], b[3]) ||
1920
- compareNumbers(a[4], b[4]) ||
1921
- compareNumbers(a[5], b[5]) ||
1897
+ naturalCompare(a[0], b[0]) ||
1898
+ naturalCompare(a[1], b[1]) ||
1899
+ naturalCompare(a[2], b[2]) ||
1922
1900
  0,
1923
1901
  );
1924
1902
 
@@ -1931,7 +1909,7 @@ export default function makeKernelKeeper(
1931
1909
  promises.push({ id: kpid, ...getKernelPromise(kpid) });
1932
1910
  }
1933
1911
  }
1934
- promises.sort((a, b) => compareStrings(a.id, b.id));
1912
+ promises.sort((a, b) => naturalCompare(a.id, b.id));
1935
1913
 
1936
1914
  const objects = [];
1937
1915
  const nextObjectID = Nat(BigInt(getRequired('ko.nextID')));
@@ -86,7 +86,7 @@ export function initializeVatState(
86
86
  /**
87
87
  * @typedef {object} VatKeeperPowers
88
88
  * @property {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts
89
- * @property {*} kernelSlog
89
+ * @property {KernelSlog} kernelSlog
90
90
  * @property {*} addKernelObject Kernel function to add a new object to the kernel's mapping tables.
91
91
  * @property {*} addKernelPromiseForVat Kernel function to add a new promise to the kernel's mapping tables.
92
92
  * @property {(kernelSlot: string) => boolean} kernelObjectExists
@@ -411,15 +411,13 @@ export function makeVatKeeper(
411
411
  // update any necessary refcounts consistently
412
412
  kvStore.set(kernelKey, buildReachableAndVatSlot(false, vatSlot));
413
413
  kvStore.set(vatKey, kernelSlot);
414
- if (kernelSlog) {
415
- kernelSlog.changeCList(
416
- vatID,
417
- getCrankNumber(),
418
- 'export',
419
- kernelSlot,
420
- vatSlot,
421
- );
422
- }
414
+ kernelSlog.changeCList(
415
+ vatID,
416
+ getCrankNumber(),
417
+ 'export',
418
+ kernelSlot,
419
+ vatSlot,
420
+ );
423
421
  kdebug(`Add mapping v->k ${kernelKey}<=>${vatKey}`);
424
422
  } else {
425
423
  // the vat didn't allocate it, and the kernel didn't allocate it
@@ -486,15 +484,13 @@ export function makeVatKeeper(
486
484
  incStat('clistEntries');
487
485
  kvStore.set(vatKey, kernelSlot);
488
486
  kvStore.set(kernelKey, buildReachableAndVatSlot(false, vatSlot));
489
- if (kernelSlog) {
490
- kernelSlog.changeCList(
491
- vatID,
492
- getCrankNumber(),
493
- 'import',
494
- kernelSlot,
495
- vatSlot,
496
- );
497
- }
487
+ kernelSlog.changeCList(
488
+ vatID,
489
+ getCrankNumber(),
490
+ 'import',
491
+ kernelSlot,
492
+ vatSlot,
493
+ );
498
494
  kdebug(`Add mapping k->v ${kernelKey}<=>${vatKey}`);
499
495
  }
500
496
 
@@ -537,15 +533,13 @@ export function makeVatKeeper(
537
533
  const vatKey = `${vatID}.c.${vatSlot}`;
538
534
  assert(kvStore.has(kernelKey));
539
535
  kdebug(`Delete mapping ${kernelKey}<=>${vatKey}`);
540
- if (kernelSlog) {
541
- kernelSlog.changeCList(
542
- vatID,
543
- getCrankNumber(),
544
- 'drop',
545
- kernelSlot,
546
- vatSlot,
547
- );
548
- }
536
+ kernelSlog.changeCList(
537
+ vatID,
538
+ getCrankNumber(),
539
+ 'drop',
540
+ kernelSlot,
541
+ vatSlot,
542
+ );
549
543
  const isExport = allocatedByVat;
550
544
  // We tolerate the object kref not being present in the kernel object
551
545
  // table, either because we're being called during the translation of
@@ -677,13 +671,7 @@ export function makeVatKeeper(
677
671
  restartWorker,
678
672
  );
679
673
 
680
- const {
681
- hash: snapshotID,
682
- uncompressedSize,
683
- dbSaveSeconds,
684
- compressedSize,
685
- compressSeconds,
686
- } = info;
674
+ const { hash: snapshotID } = info;
687
675
 
688
676
  // push a save-snapshot transcript entry
689
677
  addToTranscript(makeSaveSnapshotItem(snapshotID));
@@ -695,18 +683,6 @@ export function makeVatKeeper(
695
683
  // always starts with an initialize-worker or load-snapshot
696
684
  // pseudo-delivery
697
685
  addToTranscript(makeLoadSnapshotItem(snapshotID));
698
-
699
- kernelSlog.write({
700
- type: 'heap-snapshot-save',
701
- vatID,
702
- snapshotID,
703
- uncompressedSize,
704
- dbSaveSeconds,
705
- compressedSize,
706
- compressSeconds,
707
- endPosition,
708
- restartWorker,
709
- });
710
686
  }
711
687
 
712
688
  /**