@@ -14,6 +14,7 @@ import * as vscode from 'vscode';
1414import { Range } from 'vscode-languageclient' ;
1515import * as nls from 'vscode-nls' ;
1616import { TargetPopulation } from 'vscode-tas-client' ;
17+ import * as which from 'which' ;
1718import { logAndReturn } from '../Utility/Async/returns' ;
1819import * as util from '../common' ;
1920import { PlatformInformation } from '../platform' ;
@@ -922,8 +923,8 @@ function reportMacCrashes(): void {
922923 const crashObject : Record < string , string > = { } ;
923924 if ( err ?. code ) {
924925 // 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 ) ;
927928 return ;
928929 }
929930
@@ -959,17 +960,64 @@ function reportMacCrashes(): void {
959960 }
960961}
961962
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+ }
964974
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 {
9661006 const crashObject : Record < string , string > = { } ;
9671007 const crashCountObject : Record < string , number > = { } ;
9681008 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" ) ;
9731021}
9741022
9751023function handleMacCrashFileRead ( err : NodeJS . ErrnoException | undefined | null , data : string ) : void {
@@ -1062,6 +1110,75 @@ function handleMacCrashFileRead(err: NodeJS.ErrnoException | undefined | null, d
10621110 logMacCrashTelemetry ( data ) ;
10631111}
10641112
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+
10651182export function deactivate ( ) : Thenable < void > {
10661183 clients . timeTelemetryCollector . clear ( ) ;
10671184 console . log ( "deactivating extension" ) ;
0 commit comments