@@ -132,6 +132,7 @@ interface DebugThread {
132
132
id : number ;
133
133
line : number ;
134
134
pc : number ;
135
+ goroutineID : number ;
135
136
function ?: DebugFunction ;
136
137
}
137
138
@@ -153,6 +154,7 @@ interface DebugFunction {
153
154
goType : number ;
154
155
args : DebugVariable [ ] ;
155
156
locals : DebugVariable [ ] ;
157
+ optimized : boolean ;
156
158
}
157
159
158
160
interface ListVarsOut {
@@ -693,7 +695,7 @@ class Delve {
693
695
await this . callPromise ( 'Detach' , [ this . isApiV1 ? true : { Kill : isLocalDebugging } ] ) ;
694
696
} catch ( err ) {
695
697
log ( 'DetachResponse' ) ;
696
- logError ( ` Failed to detach - ${ err . toString ( ) || '' } ` ) ;
698
+ logError ( err , ' Failed to detach' ) ;
697
699
shouldForceClean = isLocalDebugging ;
698
700
}
699
701
}
@@ -872,7 +874,7 @@ class GoDebugSession extends LoggingDebugSession {
872
874
// We use NonBlocking so the call would return immediately.
873
875
this . debugState = await this . delve . getDebugState ( ) ;
874
876
} catch ( error ) {
875
- logError ( ` Failed to get state ${ String ( error ) } ` ) ;
877
+ this . logDelveError ( error , ' Failed to get state' ) ;
876
878
}
877
879
878
880
if ( ! this . debugState . Running && ! this . continueRequestRunning ) {
@@ -883,15 +885,13 @@ class GoDebugSession extends LoggingDebugSession {
883
885
( ) => {
884
886
return this . setBreakPoints ( response , args ) . then ( ( ) => {
885
887
return this . continue ( true ) . then ( null , ( err ) => {
886
- logError (
887
- `Failed to continue delve after halting it to set breakpoints: "${ err . toString ( ) } "`
888
- ) ;
888
+ this . logDelveError ( err , 'Failed to continue delve after halting it to set breakpoints' ) ;
889
889
} ) ;
890
890
} ) ;
891
891
} ,
892
892
( err ) => {
893
893
this . skipStopEventOnce = false ;
894
- logError ( err ) ;
894
+ this . logDelveError ( err , 'Failed to halt delve before attempting to set breakpoint' ) ;
895
895
return this . sendErrorResponse (
896
896
response ,
897
897
2008 ,
@@ -919,7 +919,7 @@ class GoDebugSession extends LoggingDebugSession {
919
919
}
920
920
921
921
if ( err ) {
922
- logError ( 'Failed to get threads - ' + err . toString ( ) ) ;
922
+ this . logDelveError ( err , 'Failed to get threads' ) ;
923
923
return this . sendErrorResponse ( response , 2003 , 'Unable to display threads: "{e}"' , {
924
924
e : err . toString ( )
925
925
} ) ;
@@ -961,7 +961,7 @@ class GoDebugSession extends LoggingDebugSession {
961
961
[ stackTraceIn ] ,
962
962
( err , out ) => {
963
963
if ( err ) {
964
- logError ( 'Failed to produce stack trace! ' ) ;
964
+ this . logDelveError ( err , 'Failed to produce stacktrace ' ) ;
965
965
return this . sendErrorResponse ( response , 2004 , 'Unable to produce stack trace: "{e}"' , {
966
966
e : err . toString ( )
967
967
} ) ;
@@ -1002,7 +1002,7 @@ class GoDebugSession extends LoggingDebugSession {
1002
1002
this . delve . isApiV1 ? [ listLocalVarsIn ] : [ { scope : listLocalVarsIn , cfg : this . delve . loadConfig } ] ,
1003
1003
( err , out ) => {
1004
1004
if ( err ) {
1005
- logError ( 'Failed to list local variables - ' + err . toString ( ) ) ;
1005
+ this . logDelveError ( err , 'Failed to get list local variables' ) ;
1006
1006
return this . sendErrorResponse ( response , 2005 , 'Unable to list locals: "{e}"' , {
1007
1007
e : err . toString ( )
1008
1008
} ) ;
@@ -1018,7 +1018,7 @@ class GoDebugSession extends LoggingDebugSession {
1018
1018
: [ { scope : listLocalFunctionArgsIn , cfg : this . delve . loadConfig } ] ,
1019
1019
( listFunctionErr , outArgs ) => {
1020
1020
if ( listFunctionErr ) {
1021
- logError ( 'Failed to list function args - ' + listFunctionErr . toString ( ) ) ;
1021
+ this . logDelveError ( listFunctionErr , 'Failed to list function args' ) ;
1022
1022
return this . sendErrorResponse ( response , 2006 , 'Unable to list args: "{e}"' , {
1023
1023
e : listFunctionErr . toString ( )
1024
1024
} ) ;
@@ -1098,7 +1098,7 @@ class GoDebugSession extends LoggingDebugSession {
1098
1098
this . delve . isApiV1 ? [ filter ] : [ { filter, cfg : this . delve . loadConfig } ] ,
1099
1099
( listPkgVarsErr , listPkgVarsOut ) => {
1100
1100
if ( listPkgVarsErr ) {
1101
- logError ( 'Failed to list global vars - ' + listPkgVarsErr . toString ( ) ) ;
1101
+ this . logDelveError ( listPkgVarsErr , 'Failed to list global vars' ) ;
1102
1102
return this . sendErrorResponse (
1103
1103
response ,
1104
1104
2007 ,
@@ -1170,7 +1170,7 @@ class GoDebugSession extends LoggingDebugSession {
1170
1170
const variable = this . delve . isApiV1 ? < DebugVariable > result : ( < EvalOut > result ) . Variable ;
1171
1171
v . children = variable . children ;
1172
1172
} ,
1173
- ( err ) => logError ( 'Failed to evaluate expression - ' + err . toString ( ) )
1173
+ ( err ) => this . logDelveError ( err , 'Failed to evaluate expression' )
1174
1174
) ;
1175
1175
}
1176
1176
} ;
@@ -1249,7 +1249,7 @@ class GoDebugSession extends LoggingDebugSession {
1249
1249
log ( 'NextRequest' ) ;
1250
1250
this . delve . call < DebuggerState | CommandOut > ( 'Command' , [ { name : 'next' } ] , ( err , out ) => {
1251
1251
if ( err ) {
1252
- logError ( 'Failed to next - ' + err . toString ( ) ) ;
1252
+ this . logDelveError ( err , 'Failed to next' ) ;
1253
1253
}
1254
1254
const state = this . delve . isApiV1 ? < DebuggerState > out : ( < CommandOut > out ) . State ;
1255
1255
log ( 'next state' , state ) ;
@@ -1264,7 +1264,7 @@ class GoDebugSession extends LoggingDebugSession {
1264
1264
log ( 'StepInRequest' ) ;
1265
1265
this . delve . call < DebuggerState | CommandOut > ( 'Command' , [ { name : 'step' } ] , ( err , out ) => {
1266
1266
if ( err ) {
1267
- logError ( 'Failed to step - ' + err . toString ( ) ) ;
1267
+ this . logDelveError ( err , 'Failed to step in' ) ;
1268
1268
}
1269
1269
const state = this . delve . isApiV1 ? < DebuggerState > out : ( < CommandOut > out ) . State ;
1270
1270
log ( 'stop state' , state ) ;
@@ -1279,7 +1279,7 @@ class GoDebugSession extends LoggingDebugSession {
1279
1279
log ( 'StepOutRequest' ) ;
1280
1280
this . delve . call < DebuggerState | CommandOut > ( 'Command' , [ { name : 'stepOut' } ] , ( err , out ) => {
1281
1281
if ( err ) {
1282
- logError ( 'Failed to stepout - ' + err . toString ( ) ) ;
1282
+ this . logDelveError ( err , 'Failed to step out' ) ;
1283
1283
}
1284
1284
const state = this . delve . isApiV1 ? < DebuggerState > out : ( < CommandOut > out ) . State ;
1285
1285
log ( 'stepout state' , state ) ;
@@ -1294,7 +1294,7 @@ class GoDebugSession extends LoggingDebugSession {
1294
1294
log ( 'PauseRequest' ) ;
1295
1295
this . delve . call < DebuggerState | CommandOut > ( 'Command' , [ { name : 'halt' } ] , ( err , out ) => {
1296
1296
if ( err ) {
1297
- logError ( 'Failed to halt - ' + err . toString ( ) ) ;
1297
+ this . logDelveError ( err , 'Failed to halt' ) ;
1298
1298
return this . sendErrorResponse ( response , 2010 , 'Unable to halt execution: "{e}"' , {
1299
1299
e : err . toString ( )
1300
1300
} ) ;
@@ -1343,7 +1343,7 @@ class GoDebugSession extends LoggingDebugSession {
1343
1343
this . delve . call ( this . delve . isApiV1 ? 'SetSymbol' : 'Set' , [ setSymbolArgs ] , ( err ) => {
1344
1344
if ( err ) {
1345
1345
const errMessage = `Failed to set variable: ${ err . toString ( ) } ` ;
1346
- logError ( errMessage ) ;
1346
+ this . logDelveError ( err , 'Failed to set variable' ) ;
1347
1347
return this . sendErrorResponse ( response , 2010 , errMessage ) ;
1348
1348
}
1349
1349
response . body = { value : args . value } ;
@@ -1741,7 +1741,7 @@ class GoDebugSession extends LoggingDebugSession {
1741
1741
// [TODO] Can we avoid doing this? https://github.com/Microsoft/vscode/issues/40#issuecomment-161999881
1742
1742
this . delve . call < DebugGoroutine [ ] | ListGoroutinesOut > ( 'ListGoroutines' , [ ] , ( err , out ) => {
1743
1743
if ( err ) {
1744
- logError ( 'Failed to get threads - ' + err . toString ( ) ) ;
1744
+ this . logDelveError ( err , 'Failed to get threads' ) ;
1745
1745
}
1746
1746
const goroutines = this . delve . isApiV1 ? < DebugGoroutine [ ] > out : ( < ListGoroutinesOut > out ) . Goroutines ;
1747
1747
this . updateGoroutinesList ( goroutines ) ;
@@ -1781,7 +1781,7 @@ class GoDebugSession extends LoggingDebugSession {
1781
1781
if ( ! calledWhenSettingBreakpoint ) {
1782
1782
errorCallback = ( err : any ) => {
1783
1783
if ( err ) {
1784
- logError ( 'Failed to continue - ' + err . toString ( ) ) ;
1784
+ this . logDelveError ( err , 'Failed to continue' ) ;
1785
1785
}
1786
1786
this . handleReenterDebug ( 'breakpoint' ) ;
1787
1787
throw err ;
@@ -1838,6 +1838,87 @@ class GoDebugSession extends LoggingDebugSession {
1838
1838
} ) ;
1839
1839
} ) ;
1840
1840
}
1841
+
1842
+ private logDelveError ( err : any , message : string ) {
1843
+ if ( err === undefined ) {
1844
+ return ;
1845
+ }
1846
+
1847
+ let errorMessage = err . toString ( ) ;
1848
+ // Handle unpropagated fatalpanic errors with a more user friendly message:
1849
+ // https://github.com/microsoft/vscode-go/issues/1903#issuecomment-460126884
1850
+ // https://github.com/go-delve/delve/issues/852
1851
+ // This affects macOS only although we're agnostic of the OS at this stage, only handle the error
1852
+ if ( errorMessage === 'bad access' ) {
1853
+ errorMessage = 'unpropagated fatalpanic: signal SIGSEGV (EXC_BAD_ACCESS). This fatalpanic is not traceable on macOS, see https://github.com/go-delve/delve/issues/852' ;
1854
+ }
1855
+
1856
+ logError ( message + ' - ' + errorMessage ) ;
1857
+
1858
+ if ( errorMessage === 'bad access' ) {
1859
+ logError ( 'WARNING: this stack might not be from the expected active goroutine' ) ;
1860
+ }
1861
+
1862
+ this . dumpStacktrace ( ) ;
1863
+ }
1864
+
1865
+ private async dumpStacktrace ( ) {
1866
+ // Get current goroutine
1867
+ // Debugger may be stopped at this point but we still can (and need) to obtain state and stacktrace
1868
+ let goroutineId = 0 ;
1869
+ try {
1870
+ const stateCallResult = await this . delve . getDebugState ( ) ;
1871
+ // In some fault scenarios there may not be a currentGoroutine available from the debugger state
1872
+ // Use the current thread
1873
+ if ( ! stateCallResult . currentGoroutine ) {
1874
+ goroutineId = stateCallResult . currentThread . goroutineID ;
1875
+ } else {
1876
+ goroutineId = stateCallResult . currentGoroutine . id ;
1877
+ }
1878
+ } catch ( error ) {
1879
+ logError ( 'dumpStacktrace - Failed to get debugger state ' + error ) ;
1880
+ }
1881
+
1882
+ // Get goroutine stacktrace
1883
+ const stackTraceIn = { id : goroutineId , depth : this . delve . stackTraceDepth } ;
1884
+ if ( ! this . delve . isApiV1 ) {
1885
+ Object . assign ( stackTraceIn , { full : false , cfg : this . delve . loadConfig } ) ;
1886
+ }
1887
+ this . delve . call < DebugLocation [ ] | StacktraceOut > (
1888
+ this . delve . isApiV1 ?
1889
+ 'StacktraceGoroutine' : 'Stacktrace' , [ stackTraceIn ] , ( err , out ) => {
1890
+ if ( err ) {
1891
+ logError ( 'dumpStacktrace: Failed to produce stack trace' + err ) ;
1892
+ return ;
1893
+ }
1894
+ const locations = this . delve . isApiV1 ? < DebugLocation [ ] > out : ( < StacktraceOut > out ) . Locations ;
1895
+ log ( 'locations' , locations ) ;
1896
+ const stackFrames = locations . map ( ( location , frameId ) => {
1897
+ const uniqueStackFrameId = this . stackFrameHandles . create ( [ goroutineId , frameId ] ) ;
1898
+ return new StackFrame (
1899
+ uniqueStackFrameId ,
1900
+ location . function ? location . function . name : '<unknown>' ,
1901
+ location . file === '<autogenerated>' ? null : new Source (
1902
+ path . basename ( location . file ) ,
1903
+ this . toLocalPath ( location . file )
1904
+ ) ,
1905
+ location . line ,
1906
+ 0
1907
+ ) ;
1908
+ } ) ;
1909
+
1910
+ // Dump stacktrace into error logger
1911
+ logError ( `Last known immediate stacktrace (goroutine id ${ goroutineId } ):` ) ;
1912
+ let output = '' ;
1913
+ stackFrames . forEach ( ( stackFrame ) => {
1914
+ output = output . concat ( `\t${ stackFrame . source . path } :${ stackFrame . line } \n` ) ;
1915
+ if ( stackFrame . name ) {
1916
+ output = output . concat ( `\t\t${ stackFrame . name } \n` ) ;
1917
+ }
1918
+ } ) ;
1919
+ logError ( output ) ;
1920
+ } ) ;
1921
+ }
1841
1922
}
1842
1923
1843
1924
function random ( low : number , high : number ) : number {
@@ -1879,4 +1960,4 @@ async function removeFile(filePath: string): Promise<void> {
1879
1960
}
1880
1961
}
1881
1962
1882
- DebugSession . run ( GoDebugSession ) ;
1963
+ DebugSession . run ( GoDebugSession ) ;
0 commit comments