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