fix(tracing): race in stopChunk (#10481)

Consider the following scenario:
- Tracing is started.
- API call is made (e.g. page.waitForResponse), almost finishes, and
  enters onAfterCall where it starts a snapshot.
- tracing.stopChunk is called, and waits for existing actions to finish.
  However, it does so by calling onAfterCall one more time.
- tracing.stopChunk removes instrumentation listener and returns
  to the client.
- Client starts zipping files.
- Original API call finishes the snapshot and saves it to the trace file.

This results in trace file being written to while the zip is still working.
This commit is contained in:
Dmitry Gozman 2021-11-22 20:08:09 -08:00 committed by GitHub
parent 80235c47a5
commit 7d3672899f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 12 additions and 11 deletions

View File

@ -172,6 +172,18 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
throw new Error(`Tracing is already stopping`);
this._isStopping = true;
if (!this._state || !this._state.recording) {
this._isStopping = false;
if (save)
throw new Error(`Must start tracing before stopping`);
return { artifact: null, entries: [] };
}
const state = this._state!;
this._context.instrumentation.removeListener(this);
if (this._state?.options.screenshots)
this._stopScreencast();
for (const { sdkObject, metadata, beforeSnapshot, actionSnapshot, afterSnapshot } of this._pendingCalls.values()) {
await Promise.all([beforeSnapshot, actionSnapshot, afterSnapshot]);
let callMetadata = metadata;
@ -185,17 +197,6 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha
await this.onAfterCall(sdkObject, callMetadata);
}
if (!this._state || !this._state.recording) {
this._isStopping = false;
if (save)
throw new Error(`Must start tracing before stopping`);
return { artifact: null, entries: [] };
}
const state = this._state!;
this._context.instrumentation.removeListener(this);
if (state.options.screenshots)
this._stopScreencast();
if (state.options.snapshots)
await this._snapshotter.stop();