From a0ba43cfae95fbe0fa44225fda6d45d53bdf67f1 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 30 May 2024 13:11:42 +0200 Subject: [PATCH] Membrane-tracing infrastructure --- packages/core/src/index.ts | 1 + packages/core/src/transport/membrane.ts | 36 ++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fc759e0..0cc563e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export * from './runtime/supervise.js'; export * as SaltyCrypto from 'salty-crypto'; export * as Cryptography from './transport/cryptography.js'; export * as WireProtocol from './transport/protocol.js'; +export * as Membrane from './transport/membrane.js'; export * as Relay from './transport/relay.js'; export * as Sturdy from './transport/sturdy.js'; diff --git a/packages/core/src/transport/membrane.ts b/packages/core/src/transport/membrane.ts index 4dd64d4..9610aad 100644 --- a/packages/core/src/transport/membrane.ts +++ b/packages/core/src/transport/membrane.ts @@ -7,6 +7,21 @@ import * as IO from '../gen/protocol.js'; import { fromCaveat, WireRef } from '../gen/sturdy.js'; import { attenuate } from '../runtime/rewrite.js'; +export type MembraneTraceEvent = { type: 'grab' | 'drop', oid: IO.Oid, ref: Ref, delta: number, newCount: number }; +export type LayerMembraneTraceEvent = MembraneTraceEvent & { table: 'exported' | 'imported' }; +export type LayerTraceEvent = + | LayerMembraneTraceEvent + | { type: 'outbound' | 'inbound', target: any, event: 'assert', assertion: Value, handle: Handle } + | { type: 'outbound' | 'inbound', event: 'retract', handle: Handle } + | { type: 'outbound' | 'inbound', event: 'message', assertion: Value } + | { type: 'outbound' | 'inbound', target: any, event: 'sync', peer: any } +; +export type LayerTracer = (e: LayerTraceEvent) => void; + +let defaultLayerTracer: LayerTracer | undefined = void 0; +export function getDefaultLayerTracer(): LayerTracer | undefined { return defaultLayerTracer; } +export function setDefaultLayerTracer(t: LayerTracer | undefined) { defaultLayerTracer = t; } + export class WireSymbol { count = 0; @@ -18,6 +33,7 @@ export class WireSymbol { drop(): void { this.count--; + this.side.tracer?.({ type: 'drop', oid: this.oid, ref: this.ref, delta: -1, newCount: this.count }); if (this.count === 0) { this.side.byOid.delete(this.oid); this.side.byRef.delete(this.ref); @@ -31,6 +47,8 @@ export class Membrane { readonly byOid = new IdentityMap(); readonly byRef = new IdentityMap(); + tracer?: (e: MembraneTraceEvent) => void; + grab(table: Table, key: Parameters[0], transient: boolean, @@ -51,6 +69,7 @@ export class Membrane { this.byOid.set(e.oid, e); } if (!transient) e.count++; + this.tracer?.({ type: 'grab', oid: e.oid, ref: e.ref, delta: transient ? 0 : +1, newCount: e.count }); return e; } } @@ -80,11 +99,19 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { readonly exported = new Membrane(); readonly imported = new Membrane(); - constructor(public trustPeer = true, public nextLocalOid: IO.Oid = 0) {} + readonly tracer: LayerTracer | undefined = defaultLayerTracer; + + constructor(public trustPeer = true, public nextLocalOid: IO.Oid = 0) { + if (this.tracer) { + this.exported.tracer = e => { const f = e as LayerMembraneTraceEvent; f.table = 'exported'; this.tracer?.(f); }; + this.imported.tracer = e => { const f = e as LayerMembraneTraceEvent; f.table = 'imported'; this.tracer?.(f); }; + } + } abstract send(remoteOid: IO.Oid, event: IO.Event>): void; proxyAssertion(targetRemoteOid: IO.Oid, assertion: Assertion, handle: Handle): Value> { + this.tracer?.({ type: 'outbound', target: targetRemoteOid, event: 'assert', assertion, handle }); const pins: Array = []; const rewritten = mapEmbeddeds(assertion, r => this.rewriteRefOut(r, false, pins)); this.grabImportedOid(targetRemoteOid, pins); @@ -93,16 +120,19 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { } proxyRetract(handle: Handle): void { + this.tracer?.({ type: 'outbound', event: 'retract', handle }); (this.outboundAssertions.get(handle) ?? []).forEach(e => e.drop()); this.outboundAssertions.delete(handle); } proxyMessage(assertion: Assertion): Value> { + this.tracer?.({ type: 'outbound', event: 'message', assertion }); const pins: Array = []; return mapEmbeddeds(assertion, r => this.rewriteRefOut(r, true, pins)); } proxySync(targetRemoteOid: IO.Oid, peer: Ref): Embedded { + this.tracer?.({ type: 'outbound', target: targetRemoteOid, event: 'sync', peer }); const peerEntity = new SyncPeerEntity(peer); this.grabImportedOid(targetRemoteOid, peerEntity.pins); return this.rewriteRefOut(Turn.ref(peerEntity), false, peerEntity.pins); @@ -192,6 +222,7 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { handle(localOid: IO.Oid, m: IO.Event>): void { switch (m._variant) { case 'Assert': { + this.tracer?.({ type: 'inbound', event: 'assert', target: localOid, assertion: m.value.assertion, handle: m.value.handle }); const [a, pins] = this.rewriteIn(m.value.assertion); const r = this.grabExportedOid(localOid, pins); this.inboundAssertions.set(m.value.handle, { @@ -201,6 +232,7 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { break; } case 'Retract': { + this.tracer?.({ type: 'inbound', event: 'retract', handle: m.value.handle }); const remoteHandle = m.value.handle; const h = this.inboundAssertions.get(remoteHandle); if (h === void 0) throw new Error(`Peer retracted invalid handle ${remoteHandle}`); @@ -210,6 +242,7 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { break; } case 'Message': { + this.tracer?.({ type: 'inbound', event: 'message', assertion: m.value.body }); const [a, pins] = this.rewriteIn(m.value.body); pins.forEach(e => { e.drop(); @@ -220,6 +253,7 @@ export abstract class LayerBoundary implements ProxyOutbound, ProxyInbound { break; } case 'Sync': { + this.tracer?.({ type: 'inbound', event: 'sync', target: localOid, peer: m.value.peer }); const pins: Array = []; const r = this.grabExportedOid(localOid, pins); const k = this.rewriteRefIn(m.value.peer, pins);