Skip to content

Commit 53ccc82

Browse files
authored
Add some basic perf logging for notebook renderers (microsoft#166649)
For microsoft#154873 Adds a new log file for the webview of a notebook. This is currently used to log the load, activation, and render times of notebook renderers
1 parent 23b25e9 commit 53ccc82

File tree

3 files changed

+86
-28
lines changed

3 files changed

+86
-28
lines changed

src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
2626
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
2727
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
2828
import { IFileService } from 'vs/platform/files/common/files';
29+
import { ILogger, ILoggerService, ILogService } from 'vs/platform/log/common/log';
2930
import { IOpenerService, matchesScheme, matchesSomeScheme } from 'vs/platform/opener/common/opener';
3031
import { IStorageService } from 'vs/platform/storage/common/storage';
3132
import { editorFindMatch, editorFindMatchHighlight } from 'vs/platform/theme/common/colorRegistry';
3233
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
3334
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
3435
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
35-
import { asWebviewUri, webviewGenericCspSource } from 'vs/workbench/contrib/webview/common/webview';
3636
import { CellEditState, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, INotebookEditorCreationOptions, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
3737
import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
3838
import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
@@ -44,8 +44,10 @@ import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/n
4444
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
4545
import { IWebviewElement, IWebviewService, WebviewContentPurpose, WebviewOriginStore } from 'vs/workbench/contrib/webview/browser/webview';
4646
import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor';
47+
import { asWebviewUri, webviewGenericCspSource } from 'vs/workbench/contrib/webview/common/webview';
4748
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
4849
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
50+
import { registerLogChannel } from 'vs/workbench/services/output/common/output';
4951
import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, StaticPreloadMetadata, ToWebviewMessage } from './webviewMessages';
5052

5153
export interface ICachedInset<K extends ICommonCellInfo> {
@@ -99,6 +101,8 @@ interface BacklayerWebviewOptions {
99101
readonly outputLineHeight: number;
100102
}
101103

104+
const logChannelId = 'notebook.rendering';
105+
102106
export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
103107

104108
private static _originStore?: WebviewOriginStore;
@@ -126,6 +130,8 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
126130
private firstInit = true;
127131
private initializeMarkupPromise?: { readonly requestId: string; readonly p: DeferredPromise<void>; readonly isFirstInit: boolean };
128132

133+
private readonly _renderLogger: ILogger;
134+
129135
private readonly nonce = UUID.generateUuid();
130136

131137
constructor(
@@ -150,10 +156,18 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
150156
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
151157
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
152158
@IStorageService private readonly storageService: IStorageService,
159+
@ILoggerService loggerService: ILoggerService,
160+
@ILogService logService: ILogService,
153161
@IThemeService themeService: IThemeService,
154162
) {
155163
super(themeService);
156164

165+
const logsPath = joinPath(environmentService.windowLogsPath, 'notebook.rendering.log');
166+
this._renderLogger = this._register(loggerService.createLogger(logsPath, { name: 'notebook.rendering' }));
167+
registerLogChannel(logChannelId, nls.localize('renderChannelName', "Notebook rendering"), logsPath, fileService, logService);
168+
169+
this._logRendererDebugMessage('Creating backlayer webview for notebook');
170+
157171
this.element = document.createElement('div');
158172

159173
this.element.style.height = '1400px';
@@ -198,6 +212,10 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
198212
this._updateOptions();
199213
}
200214

215+
private _logRendererDebugMessage(msg: string) {
216+
this._renderLogger.debug(`${this.id} - ${msg}`);
217+
}
218+
201219
private _updateStyles() {
202220
this._sendMessageToWebview({
203221
type: 'notebookStyles',
@@ -861,6 +879,10 @@ var requirejs = (function() {
861879
});
862880
break;
863881
}
882+
case 'logRendererDebugMessage': {
883+
this._logRendererDebugMessage(`${data.message}${data.data ? ' ' + JSON.stringify(data.data, null, 4) : ''}`);
884+
break;
885+
}
864886
}
865887
}));
866888
}

src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,12 @@ export interface IReturnOutputItemMessage {
446446
readonly output: OutputItemEntry | undefined;
447447
}
448448

449+
export interface ILogRendererDebugMessage extends BaseToWebviewMessage {
450+
readonly type: 'logRendererDebugMessage';
451+
readonly message: string;
452+
readonly data?: Record<string, string>;
453+
}
454+
449455

450456
export type FromWebviewMessage = WebviewInitialized |
451457
IDimensionMessage |
@@ -476,7 +482,8 @@ export type FromWebviewMessage = WebviewInitialized |
476482
IDidFindMessage |
477483
IDidFindHighlightMessage |
478484
IOutputResizedMessage |
479-
IGetOutputItemMessage;
485+
IGetOutputItemMessage |
486+
ILogRendererDebugMessage;
480487

481488
export type ToWebviewMessage = IClearMessage |
482489
IFocusOutputMessage |

src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,10 +1344,15 @@ async function webviewPreloads(ctx: PreloadContext) {
13441344
}
13451345

13461346
try {
1347+
const renderStart = performance.now();
13471348
await this._api.renderOutputItem(item, element, signal);
1349+
this.postDebugMessage('Rendered output item', { id: item.id, duration: `${performance.now() - renderStart}ms` });
1350+
13481351
} catch (e) {
13491352
if (!signal.aborted) {
13501353
showRenderError(`Error rendering output item using '${this.data.id}'`, element, e instanceof Error ? [e] : []);
1354+
1355+
this.postDebugMessage('Rendering output item failed', { id: item.id, error: e + '' });
13511356
}
13521357
}
13531358
}
@@ -1397,36 +1402,60 @@ async function webviewPreloads(ctx: PreloadContext) {
13971402

13981403
/** Inner function cached in the _loadPromise(). */
13991404
private async _load(): Promise<rendererApi.RendererApi | undefined> {
1400-
// Preloads need to be loaded before loading renderers.
1401-
await kernelPreloads.waitForAllCurrent();
1405+
this.postDebugMessage('Start loading renderer');
14021406

1403-
const module: RendererModule = await __import(this.data.entrypoint.path);
1404-
if (!module) {
1405-
return;
1406-
}
1407+
try {
1408+
// Preloads need to be loaded before loading renderers.
1409+
await kernelPreloads.waitForAllCurrent();
14071410

1408-
this._api = await module.activate(this.createRendererContext());
1409-
// Load all renderers that extend this renderer
1410-
await Promise.all(
1411-
ctx.rendererData
1412-
.filter(d => d.entrypoint.extends === this.data.id)
1413-
.map(async d => {
1414-
const renderer = renderers.getRenderer(d.id);
1415-
if (!renderer) {
1416-
throw new Error(`Could not find extending renderer: ${d.id}`);
1417-
}
1411+
const importStart = performance.now();
1412+
const module: RendererModule = await __import(this.data.entrypoint.path);
1413+
this.postDebugMessage('Imported renderer', { duration: `${performance.now() - importStart}ms` });
14181414

1419-
try {
1420-
return await renderer.load();
1421-
} catch (e) {
1422-
// Squash any errors extends errors. They won't prevent the renderer
1423-
// itself from working, so just log them.
1424-
console.error(e);
1425-
return undefined;
1426-
}
1427-
}));
1415+
if (!module) {
1416+
return;
1417+
}
1418+
1419+
this._api = await module.activate(this.createRendererContext());
1420+
this.postDebugMessage('Activated renderer', { duration: `${performance.now() - importStart}ms` });
1421+
1422+
const dependantRenderers = ctx.rendererData
1423+
.filter(d => d.entrypoint.extends === this.data.id);
1424+
1425+
if (dependantRenderers.length) {
1426+
this.postDebugMessage('Activating dependant renderers', { dependents: dependantRenderers.map(x => x.id).join(', ') });
1427+
}
14281428

1429-
return this._api;
1429+
// Load all renderers that extend this renderer
1430+
await Promise.all(dependantRenderers.map(async d => {
1431+
const renderer = renderers.getRenderer(d.id);
1432+
if (!renderer) {
1433+
throw new Error(`Could not find extending renderer: ${d.id}`);
1434+
}
1435+
1436+
try {
1437+
return await renderer.load();
1438+
} catch (e) {
1439+
// Squash any errors extends errors. They won't prevent the renderer
1440+
// itself from working, so just log them.
1441+
console.error(e);
1442+
this.postDebugMessage('Activating dependant renderer failed', { dependent: d.id, error: e + '' });
1443+
return undefined;
1444+
}
1445+
}));
1446+
1447+
return this._api;
1448+
} catch (e) {
1449+
this.postDebugMessage('Loading renderer failed');
1450+
throw e;
1451+
}
1452+
}
1453+
1454+
private postDebugMessage(msg: string, data?: Record<string, string>) {
1455+
postNotebookMessage<webviewMessages.ILogRendererDebugMessage>('logRendererDebugMessage', {
1456+
message: `[renderer ${this.data.id}] - ${msg}`,
1457+
data
1458+
});
14301459
}
14311460
}
14321461

0 commit comments

Comments
 (0)