Skip to content

Commit

Permalink
Support loading additional supplemental recordings
Browse files Browse the repository at this point in the history
  • Loading branch information
bhackett1024 committed Sep 11, 2024
1 parent 7142c55 commit f78b2b0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 30 deletions.
1 change: 0 additions & 1 deletion packages/protocol/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ let gSessionCallbacks: SessionCallbacks | undefined;

export function setSessionCallbacks(sessionCallbacks: SessionCallbacks) {
if (gSessionCallbacks !== undefined) {
console.error("Session callbacks can only be set once");
return;
}

Expand Down
83 changes: 57 additions & 26 deletions packages/shared/client/ReplayClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ import uniqueId from "lodash/uniqueId";

// eslint-disable-next-line no-restricted-imports
import { addEventListener, client, initSocket, removeEventListener } from "protocol/socket";
import { assert, compareNumericStrings, defer, waitForTime } from "protocol/utils";
import { assert, compareNumericStrings, defer, Deferred, waitForTime } from "protocol/utils";
import { initProtocolMessagesStore } from "replay-next/components/protocol/ProtocolMessagesStore";
import { insert } from "replay-next/src/utils/array";
import { TOO_MANY_POINTS_TO_FIND } from "shared/constants";
Expand All @@ -90,6 +90,7 @@ import {
ReplayClientEvents,
ReplayClientInterface,
SourceLocationRange,
SupplementalSession,
TimeStampedPointWithPaintHash,
} from "./types";

Expand All @@ -111,6 +112,8 @@ export class ReplayClient implements ReplayClientInterface {

private sessionWaiter = defer<SessionId>();

private supplemental: SupplementalSession[] = [];

private focusWindow: TimeStampedPointRange | null = null;

private nextFindPointsId = 1;
Expand All @@ -131,18 +134,42 @@ export class ReplayClient implements ReplayClientInterface {
// Configures the client to use an already initialized session iD.
// This method should be used for apps that use the protocol package directly.
// Apps that only communicate with the Replay protocol through this client should use the initialize method instead.
async configure(sessionId: string): Promise<void> {
async configure(sessionId: string, supplemental: SupplementalSession[]): Promise<void> {
this._sessionId = sessionId;
this._dispatchEvent("sessionCreated");
this.sessionWaiter.resolve(sessionId);

this.supplemental.push(...supplemental);
await this.syncFocusWindow();
}

waitForSession() {
return this.sessionWaiter.promise;
}

private async forEachSession(callback: (sessionId: string, supplementalIndex: number) => Promise<void>) {
const sessionId = await this.waitForSession();
await callback(sessionId, 0);
for (let i = 0; i < this.supplemental.length; i++) {
await callback(this.supplemental[i].sessionId, i + 1);
}
}

private transformSupplementalId(id: string, supplementalIndex: number) {
return `s${supplementalIndex}-${id}`;
}

private async breakdownSupplementalId(id: string): Promise<{ id: string, sessionId: string }> {
const match = /^s(\d+)-(.*)/.exec(id);
if (!match) {
const sessionId = await this.waitForSession();
return { id, sessionId };
}
const supplementalIndex = +match[1];
const supplementalInfo = this.supplemental[supplementalIndex - 1];
assert(supplementalInfo);
return { id: match[2], sessionId: supplementalInfo.sessionId };
}

get loadedRegions(): LoadedRegions | null {
return this._loadedRegions;
}
Expand Down Expand Up @@ -508,24 +535,26 @@ export class ReplayClient implements ReplayClientInterface {
async findSources(): Promise<Source[]> {
const sources: Source[] = [];

await this.waitForSession();

const sessionId = await this.waitForSession();

const newSourceListener = (source: Source) => {
sources.push(source);
};
const newSourcesListener = ({ sources: sourcesList }: newSources) => {
for (const source of sourcesList) {
await this.forEachSession(async (sessionId, supplementalIndex) => {
const newSourceListener = (source: Source) => {
sources.push(source);
}
};
};
const newSourcesListener = ({ sources: sourcesList }: newSources) => {
for (const source of sourcesList) {
if (supplementalIndex) {
source.sourceId = this.transformSupplementalId(source.sourceId, supplementalIndex);
source.generatedSourceIds = source.generatedSourceIds?.map(id => this.transformSupplementalId(id, supplementalIndex));
}
sources.push(source);
}
};

client.Debugger.addNewSourceListener(newSourceListener);
client.Debugger.addNewSourcesListener(newSourcesListener);
await client.Debugger.findSources({}, sessionId);
client.Debugger.removeNewSourceListener(newSourceListener);
client.Debugger.removeNewSourcesListener(newSourcesListener);
client.Debugger.addNewSourceListener(newSourceListener);
client.Debugger.addNewSourcesListener(newSourcesListener);
await client.Debugger.findSources({}, sessionId);
client.Debugger.removeNewSourceListener(newSourceListener);
client.Debugger.removeNewSourcesListener(newSourcesListener);
});

return sources;
}
Expand Down Expand Up @@ -796,14 +825,15 @@ export class ReplayClient implements ReplayClientInterface {
getSessionId = (): SessionId | null => this._sessionId;

async getSourceHitCounts(
sourceId: SourceId,
transformedSourceId: SourceId,
locations: SameLineSourceLocations[],
focusRange: PointRange | null
) {
const sessionId = await this.waitForSession();
const { id: sourceId, sessionId } = await this.breakdownSupplementalId(transformedSourceId);

await this.waitForRangeToBeInFocusRange(focusRange);
const { hits } = await client.Debugger.getHitCounts(
{ sourceId, locations, maxHits: TOO_MANY_POINTS_TO_FIND, range: focusRange || undefined },
{ sourceId, locations, maxHits: TOO_MANY_POINTS_TO_FIND, range: /*focusRange ||*/ undefined },
sessionId
);
return hits;
Expand All @@ -815,10 +845,11 @@ export class ReplayClient implements ReplayClientInterface {
}

async getBreakpointPositions(
sourceId: SourceId,
transformedSourceId: SourceId,
locationRange: SourceLocationRange | null
): Promise<SameLineSourceLocations[]> {
const sessionId = await this.waitForSession();
const { id: sourceId, sessionId } = await this.breakdownSupplementalId(transformedSourceId);

const begin = locationRange ? locationRange.start : undefined;
const end = locationRange ? locationRange.end : undefined;

Expand Down Expand Up @@ -1063,11 +1094,11 @@ export class ReplayClient implements ReplayClientInterface {
}

async streamSourceContents(
sourceId: SourceId,
transformedSourceId: SourceId,
onSourceContentsInfo: (params: sourceContentsInfo) => void,
onSourceContentsChunk: (params: sourceContentsChunk) => void
): Promise<void> {
const sessionId = await this.waitForSession();
const { id: sourceId, sessionId } = await this.breakdownSupplementalId(transformedSourceId);

let pendingChunk = "";
let pendingThrottlePromise: Promise<void> | null = null;
Expand Down
10 changes: 9 additions & 1 deletion packages/shared/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,18 @@ export interface TimeStampedPointWithPaintHash extends TimeStampedPoint {

export type AnnotationListener = (annotation: Annotation) => void;

export interface SupplementalRecording {
recordingId: string;
}

export interface SupplementalSession extends SupplementalRecording {
sessionId: string;
}

export interface ReplayClientInterface {
get loadedRegions(): LoadedRegions | null;
addEventListener(type: ReplayClientEvents, handler: Function): void;
configure(sessionId: string): Promise<void>;
configure(sessionId: string, supplemental: SupplementalSession[]): Promise<void>;
createPause(executionPoint: ExecutionPoint): Promise<createPauseResult>;
evaluateExpression(
pauseId: PauseId,
Expand Down
23 changes: 21 additions & 2 deletions src/ui/actions/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { subscriptionExpired } from "ui/utils/workspace";
import { setExpectedError, setUnexpectedError } from "./errors";
import { setToolboxLayout, setViewMode } from "./layout";
import { jumpToInitialPausePoint } from "./timeline";
import { SupplementalRecording } from "shared/client/types";

export { setExpectedError, setUnexpectedError };

Expand Down Expand Up @@ -90,6 +91,14 @@ export function getAccessibleRecording(
};
}

function getSupplementalRecordings(recordingId: string): SupplementalRecording[] {
switch (recordingId) {
case "d5513383-5986-4de5-ab9d-2a7e1f367e90":
return [{ recordingId: "c54962d6-9ac6-428a-a6af-2bb2bf6633ca" }];
}
return [];
}

function clearRecordingNotAccessibleError(): UIThunkAction {
return async (dispatch, getState) => {
const state = getState();
Expand Down Expand Up @@ -274,7 +283,7 @@ export function createSocket(recordingId: string): UIThunkAction {
})
);

const sessionId = await createSession(
const doCreateSession = (recordingId: string) => createSession(
recordingId,
experimentalSettings,
focusWindowFromURL !== null ? focusWindowFromURL : undefined,
Expand Down Expand Up @@ -353,12 +362,22 @@ export function createSocket(recordingId: string): UIThunkAction {
}
);

const sessionId = await doCreateSession(recordingId);
console.log("MainSessionId", JSON.stringify({ recordingId, sessionId }));

const supplementalRecordings = getSupplementalRecordings(recordingId);
const supplemental = await Promise.all(supplementalRecordings.map(async ({ recordingId }) => {
const sessionId = await doCreateSession(recordingId);
console.log("SupplementalSessionId", JSON.stringify({ recordingId, sessionId }));
return { recordingId, sessionId };
}));

Sentry.configureScope(scope => {
scope.setExtra("sessionId", sessionId);
});

window.sessionId = sessionId;
await replayClient.configure(sessionId);
await replayClient.configure(sessionId, supplemental);
const recordingTarget = await recordingTargetCache.readAsync(replayClient);
dispatch(actions.setRecordingTarget(recordingTarget));

Expand Down

0 comments on commit f78b2b0

Please sign in to comment.