src/vs/workbench/api/common/extensionHostMain.ts/ErrorHandler/installFullHandler
class ErrorHandler:	static async installFullHandler(accessor: ServicesAccessor): Promise<void> {
		// uses extension knowledges to correlate errors with extensions

		const logService = accessor.get(ILogService);
		const rpcService = accessor.get(IExtHostRpcService);
		const extensionService = accessor.get(IExtHostExtensionService);
		const extensionTelemetry = accessor.get(IExtHostTelemetry);

		const mainThreadExtensions = rpcService.getProxy(MainContext.MainThreadExtensionService);
		const mainThreadErrors = rpcService.getProxy(MainContext.MainThreadErrors);

		const map = await extensionService.getExtensionPathIndex();
		const extensionErrors = new WeakMap<Error, ExtensionIdentifier | undefined>();

		// set the prepareStackTrace-handle and use it as a side-effect to associate errors
		// with extensions - this works by looking up callsites in the extension path index
		(<any>Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => {
			let stackTraceMessage = '';
			let extension: IExtensionDescription | undefined;
			let fileName: string | null;
			for (const call of stackTrace) {
				stackTraceMessage += `\n\tat ${call.toString()}`;
				fileName = call.getFileName();
				if (!extension && fileName) {
					extension = map.findSubstr(URI.file(fileName));
				}
			}
			extensionErrors.set(error, extension?.identifier);
			return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`;
		};

		errors.setUnexpectedErrorHandler(err => {
			logService.error(err);

			const data = errors.transformErrorForSerialization(err);
			const extension = extensionErrors.get(err);
			if (!extension) {
				mainThreadErrors.$onUnexpectedError(data);
				return;
			}

			mainThreadExtensions.$onExtensionRuntimeError(extension, data);
			const reported = extensionTelemetry.onExtensionError(extension, err);
			logService.trace('forwarded error to extension?', reported, extension);
		});
	}