@@ -11,16 +11,17 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
11
11
import { MainContext , MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol' ;
12
12
import { IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol' ;
13
13
import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol' ;
14
- import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' ;
14
+ import { ExtensionIdentifier , IExtensionDescription } from 'vs/platform/extensions/common/extensions' ;
15
15
import { ILogService } from 'vs/platform/log/common/log' ;
16
16
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions' ;
17
17
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection' ;
18
18
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService' ;
19
19
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService' ;
20
- import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
20
+ import { IInstantiationService , ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' ;
21
21
import { IExtHostRpcService , ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService' ;
22
22
import { IURITransformerService , URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService' ;
23
23
import { IExtHostExtensionService , IHostUtils } from 'vs/workbench/api/common/extHostExtensionService' ;
24
+ import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry' ;
24
25
25
26
export interface IExitFn {
26
27
( code ?: number ) : any ;
@@ -30,6 +31,75 @@ export interface IConsolePatchFn {
30
31
( mainThreadConsole : MainThreadConsoleShape ) : any ;
31
32
}
32
33
34
+ abstract class ErrorHandler {
35
+
36
+ static {
37
+ // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
38
+ Error . stackTraceLimit = 100 ;
39
+ }
40
+
41
+ static async installEarlyHandler ( accessor : ServicesAccessor ) : Promise < void > {
42
+ // does NOT dependent of extension information, can be installed immediately, and simply forwards
43
+ // to the log service and main thread errors
44
+ const logService = accessor . get ( ILogService ) ;
45
+ const rpcService = accessor . get ( IExtHostRpcService ) ;
46
+ const mainThreadErrors = rpcService . getProxy ( MainContext . MainThreadErrors ) ;
47
+
48
+ errors . setUnexpectedErrorHandler ( err => {
49
+ logService . error ( err ) ;
50
+ const data = errors . transformErrorForSerialization ( err ) ;
51
+ mainThreadErrors . $onUnexpectedError ( data ) ;
52
+ } ) ;
53
+ }
54
+
55
+ static async installFullHandler ( accessor : ServicesAccessor ) : Promise < void > {
56
+ // uses extension knowledges to correlate errors with extensions
57
+
58
+ const logService = accessor . get ( ILogService ) ;
59
+ const rpcService = accessor . get ( IExtHostRpcService ) ;
60
+ const extensionService = accessor . get ( IExtHostExtensionService ) ;
61
+ const extensionTelemetry = accessor . get ( IExtHostTelemetry ) ;
62
+
63
+ const mainThreadExtensions = rpcService . getProxy ( MainContext . MainThreadExtensionService ) ;
64
+ const mainThreadErrors = rpcService . getProxy ( MainContext . MainThreadErrors ) ;
65
+
66
+ const map = await extensionService . getExtensionPathIndex ( ) ;
67
+ const extensionErrors = new WeakMap < Error , ExtensionIdentifier | undefined > ( ) ;
68
+
69
+ // set the prepareStackTrace-handle and use it as a side-effect to associate errors
70
+ // with extensions - this works by looking up callsites in the extension path index
71
+ ( < any > Error ) . prepareStackTrace = ( error : Error , stackTrace : errors . V8CallSite [ ] ) => {
72
+ let stackTraceMessage = '' ;
73
+ let extension : IExtensionDescription | undefined ;
74
+ let fileName : string | null ;
75
+ for ( const call of stackTrace ) {
76
+ stackTraceMessage += `\n\tat ${ call . toString ( ) } ` ;
77
+ fileName = call . getFileName ( ) ;
78
+ if ( ! extension && fileName ) {
79
+ extension = map . findSubstr ( URI . file ( fileName ) ) ;
80
+ }
81
+ }
82
+ extensionErrors . set ( error , extension ?. identifier ) ;
83
+ return `${ error . name || 'Error' } : ${ error . message || '' } ${ stackTraceMessage } ` ;
84
+ } ;
85
+
86
+ errors . setUnexpectedErrorHandler ( err => {
87
+ logService . error ( err ) ;
88
+
89
+ const data = errors . transformErrorForSerialization ( err ) ;
90
+ const extension = extensionErrors . get ( err ) ;
91
+ if ( ! extension ) {
92
+ mainThreadErrors . $onUnexpectedError ( data ) ;
93
+ return ;
94
+ }
95
+
96
+ mainThreadExtensions . $onExtensionRuntimeError ( extension , data ) ;
97
+ const reported = extensionTelemetry . onExtensionError ( extension , err ) ;
98
+ logService . trace ( 'forwarded error to extension?' , reported , extension ) ;
99
+ } ) ;
100
+ }
101
+ }
102
+
33
103
export class ExtensionHostMain {
34
104
35
105
private readonly _hostUtils : IHostUtils ;
@@ -59,6 +129,8 @@ export class ExtensionHostMain {
59
129
60
130
const instaService : IInstantiationService = new InstantiationService ( services , true ) ;
61
131
132
+ instaService . invokeFunction ( ErrorHandler . installEarlyHandler ) ;
133
+
62
134
// ugly self - inject
63
135
this . _logService = instaService . invokeFunction ( accessor => accessor . get ( ILogService ) ) ;
64
136
@@ -76,38 +148,8 @@ export class ExtensionHostMain {
76
148
this . _extensionService = instaService . invokeFunction ( accessor => accessor . get ( IExtHostExtensionService ) ) ;
77
149
this . _extensionService . initialize ( ) ;
78
150
79
- // error forwarding and stack trace scanning
80
- Error . stackTraceLimit = 100 ; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
81
- const extensionErrors = new WeakMap < Error , IExtensionDescription | undefined > ( ) ;
82
- this . _extensionService . getExtensionPathIndex ( ) . then ( map => {
83
- ( < any > Error ) . prepareStackTrace = ( error : Error , stackTrace : errors . V8CallSite [ ] ) => {
84
- let stackTraceMessage = '' ;
85
- let extension : IExtensionDescription | undefined ;
86
- let fileName : string ;
87
- for ( const call of stackTrace ) {
88
- stackTraceMessage += `\n\tat ${ call . toString ( ) } ` ;
89
- fileName = call . getFileName ( ) ;
90
- if ( ! extension && fileName ) {
91
- extension = map . findSubstr ( URI . file ( fileName ) ) ;
92
- }
93
-
94
- }
95
- extensionErrors . set ( error , extension ) ;
96
- return `${ error . name || 'Error' } : ${ error . message || '' } ${ stackTraceMessage } ` ;
97
- } ;
98
- } ) ;
99
-
100
- const mainThreadExtensions = this . _rpcProtocol . getProxy ( MainContext . MainThreadExtensionService ) ;
101
- const mainThreadErrors = this . _rpcProtocol . getProxy ( MainContext . MainThreadErrors ) ;
102
- errors . setUnexpectedErrorHandler ( err => {
103
- const data = errors . transformErrorForSerialization ( err ) ;
104
- const extension = extensionErrors . get ( err ) ;
105
- if ( extension ) {
106
- mainThreadExtensions . $onExtensionRuntimeError ( extension . identifier , data ) ;
107
- } else {
108
- mainThreadErrors . $onUnexpectedError ( data ) ;
109
- }
110
- } ) ;
151
+ // install error handler that is extension-aware
152
+ instaService . invokeFunction ( ErrorHandler . installFullHandler ) ;
111
153
}
112
154
113
155
async asBrowserUri ( uri : URI ) : Promise < URI > {
0 commit comments