@@ -14,6 +14,7 @@ import * as vscode from 'vscode';
14
14
import { Range } from 'vscode-languageclient' ;
15
15
import * as nls from 'vscode-nls' ;
16
16
import { TargetPopulation } from 'vscode-tas-client' ;
17
+ import * as which from 'which' ;
17
18
import { logAndReturn } from '../Utility/Async/returns' ;
18
19
import * as util from '../common' ;
19
20
import { PlatformInformation } from '../platform' ;
@@ -922,8 +923,8 @@ function reportMacCrashes(): void {
922
923
const crashObject : Record < string , string > = { } ;
923
924
if ( err ?. code ) {
924
925
// If the directory isn't there, we have a problem...
925
- crashObject [ "fs.stat: err.code " ] = err . code ;
926
- telemetry . logLanguageServerEvent ( "MacCrash" , crashObject , undefined ) ;
926
+ crashObject [ "errCode " ] = err . code ;
927
+ telemetry . logLanguageServerEvent ( "MacCrash" , crashObject ) ;
927
928
return ;
928
929
}
929
930
@@ -959,17 +960,64 @@ function reportMacCrashes(): void {
959
960
}
960
961
}
961
962
962
- let previousMacCrashData : string ;
963
- let previousMacCrashCount : number = 0 ;
963
+ export function watchForCrashes ( crashDirectory : string ) : void {
964
+ if ( process . platform !== "win32" ) {
965
+ prevCrashFile = "" ;
966
+ fs . stat ( crashDirectory , ( err ) => {
967
+ const crashObject : Record < string , string > = { } ;
968
+ if ( err ?. code ) {
969
+ // If the directory isn't there, we have a problem...
970
+ crashObject [ "errCode" ] = err . code ;
971
+ telemetry . logLanguageServerEvent ( "CppCrash" , crashObject ) ;
972
+ return ;
973
+ }
964
974
965
- function logMacCrashTelemetry ( data : string ) : void {
975
+ // vscode.workspace.createFileSystemWatcher only works in workspace folders.
976
+ try {
977
+ fs . watch ( crashDirectory , ( event , filename ) => {
978
+ if ( event !== "rename" ) {
979
+ return ;
980
+ }
981
+ if ( ! filename || filename === prevCrashFile ) {
982
+ return ;
983
+ }
984
+ prevCrashFile = filename ;
985
+ if ( ! filename . startsWith ( "cpptools" ) ) {
986
+ return ;
987
+ }
988
+ // Wait 5 seconds to allow time for the crash log to finish being written.
989
+ setTimeout ( ( ) => {
990
+ fs . readFile ( path . resolve ( crashDirectory , filename ) , 'utf8' , ( err , data ) => {
991
+ void handleCrashFileRead ( crashDirectory , filename , err , data ) ;
992
+ } ) ;
993
+ } , 5000 ) ;
994
+ } ) ;
995
+ } catch ( e ) {
996
+ // The file watcher limit is hit (may not be possible on Mac, but just in case).
997
+ }
998
+ } ) ;
999
+ }
1000
+ }
1001
+
1002
+ let previousCrashData : string ;
1003
+ let previousCrashCount : number = 0 ;
1004
+
1005
+ function logCrashTelemetry ( data : string , type : string ) : void {
966
1006
const crashObject : Record < string , string > = { } ;
967
1007
const crashCountObject : Record < string , number > = { } ;
968
1008
crashObject . CrashingThreadCallStack = data ;
969
- previousMacCrashCount = data === previousMacCrashData ? previousMacCrashCount + 1 : 0 ;
970
- previousMacCrashData = data ;
971
- crashCountObject . CrashCount = previousMacCrashCount ;
972
- telemetry . logLanguageServerEvent ( "MacCrash" , crashObject , crashCountObject ) ;
1009
+ previousCrashCount = data === previousCrashData ? previousCrashCount + 1 : 0 ;
1010
+ previousCrashData = data ;
1011
+ crashCountObject . CrashCount = previousCrashCount + 1 ;
1012
+ telemetry . logLanguageServerEvent ( type , crashObject , crashCountObject ) ;
1013
+ }
1014
+
1015
+ function logMacCrashTelemetry ( data : string ) : void {
1016
+ logCrashTelemetry ( data , "MacCrash" ) ;
1017
+ }
1018
+
1019
+ function logCppCrashTelemetry ( data : string ) : void {
1020
+ logCrashTelemetry ( data , "CppCrash" ) ;
973
1021
}
974
1022
975
1023
function handleMacCrashFileRead ( err : NodeJS . ErrnoException | undefined | null , data : string ) : void {
@@ -1062,6 +1110,75 @@ function handleMacCrashFileRead(err: NodeJS.ErrnoException | undefined | null, d
1062
1110
logMacCrashTelemetry ( data ) ;
1063
1111
}
1064
1112
1113
+ async function handleCrashFileRead ( crashDirectory : string , crashFile : string , err : NodeJS . ErrnoException | undefined | null , data : string ) : Promise < void > {
1114
+ if ( err ) {
1115
+ if ( err . code === "ENOENT" ) {
1116
+ return ; // ignore known issue
1117
+ }
1118
+ return logCppCrashTelemetry ( "readFile: " + err . code ) ;
1119
+ }
1120
+
1121
+ const lines : string [ ] = data . split ( "\n" ) ;
1122
+ data = crashFile + "\n" ;
1123
+ const filtPath : string | null = which . sync ( "c++filt" , { nothrow : true } ) ;
1124
+ const isMac : boolean = process . platform === "darwin" ;
1125
+ const startStr : string = isMac ? " _" : "(_" ;
1126
+ const offsetStr : string = isMac ? " + " : "+0x" ;
1127
+ const dotStr : string = "…" ;
1128
+ for ( let lineNum : number = 2 ; lineNum < lines . length - 3 ; ++ lineNum ) { // skip first/last lines
1129
+ if ( lineNum > 2 ) {
1130
+ data += "\n" ;
1131
+ }
1132
+ const line : string = lines [ lineNum ] ;
1133
+ const startPos : number = line . indexOf ( startStr ) ;
1134
+ if ( startPos === - 1 ) {
1135
+ data += dotStr ;
1136
+ continue ; // expected
1137
+ }
1138
+ const offsetPos : number = line . indexOf ( offsetStr , startPos + startStr . length ) ;
1139
+ if ( offsetPos === - 1 ) {
1140
+ data += `missing "${ offsetStr } "` ;
1141
+ continue ; // unexpected
1142
+ }
1143
+ const startPos2 : number = startPos + 1 ;
1144
+ let funcStr : string = line . substring ( startPos2 , offsetPos ) ;
1145
+ if ( filtPath ) {
1146
+ const ret = await util . spawnChildProcess ( filtPath , [ funcStr ] , undefined , true ) . catch ( logAndReturn . undefined ) ;
1147
+ if ( ret !== undefined ) {
1148
+ funcStr = ret . output ;
1149
+ funcStr = funcStr . replace ( / s t d : : (?: _ _ 1 | _ _ c x x 1 1 ) / g, "std" ) ; // simplify std namespaces.
1150
+ funcStr = funcStr . replace ( / s t d : : b a s i c _ / g, "std::" ) ;
1151
+ funcStr = funcStr . replace ( / > / g, ">" ) ;
1152
+ funcStr = funcStr . replace ( / , s t d : : (?: a l l o c a t o r | c h a r _ t r a i t s ) < c h a r > / g, "" ) ;
1153
+ funcStr = funcStr . replace ( / < c h a r > / g, "" ) ;
1154
+ funcStr = funcStr . replace ( / , s t d : : a l l o c a t o r < s t d : : s t r i n g > / g, "" ) ;
1155
+ }
1156
+ }
1157
+ data += funcStr + offsetStr ;
1158
+ const offsetPos2 : number = offsetPos + offsetStr . length ;
1159
+ if ( isMac ) {
1160
+ data += line . substring ( offsetPos2 ) ;
1161
+ } else {
1162
+ const endPos : number = line . indexOf ( ")" , offsetPos2 ) ;
1163
+ if ( endPos === - 1 ) {
1164
+ data += "missing )" ;
1165
+ continue ; // unexpected
1166
+ }
1167
+ data += line . substring ( offsetPos2 , endPos ) ;
1168
+ }
1169
+ }
1170
+
1171
+ if ( data . length > 8192 ) { // The API has an 8k limit.
1172
+ data = data . substring ( 0 , 8191 ) + "…" ;
1173
+ }
1174
+
1175
+ console . log ( `Crash call stack:\n${ data } ` ) ;
1176
+ logCppCrashTelemetry ( data ) ;
1177
+
1178
+ await util . deleteFile ( path . resolve ( crashDirectory , crashFile ) ) . catch ( logAndReturn . undefined ) ;
1179
+ void util . deleteDirectory ( crashDirectory ) . catch ( logAndReturn . undefined ) ;
1180
+ }
1181
+
1065
1182
export function deactivate ( ) : Thenable < void > {
1066
1183
clients . timeTelemetryCollector . clear ( ) ;
1067
1184
console . log ( "deactivating extension" ) ;
0 commit comments