service/dap: implement exception info (#2444)
* service/dap: implement exception info * remove adding additional thread * Fix tests * add exceptionInfo tests * update comments * map paths to client paths * remove launch.json * remove change to ConvertEvalScope * correct name of supportsExceptionInfoRequest * Add TODO for deleting output event * Print Stack header to buffer * Try to move resolving exception info to onExceptionInfoRequest * save the error and return if it is the current thread * rename thread to g * findgoroutine returns goroutine * clean up findgoroutine * log errors * remove output event * fix grammar
This commit is contained in:
parent
1e9c5c3b07
commit
30cdedae69
@ -2279,6 +2279,10 @@ func digits(n int) int {
|
|||||||
const stacktraceTruncatedMessage = "(truncated)"
|
const stacktraceTruncatedMessage = "(truncated)"
|
||||||
|
|
||||||
func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
|
func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
|
||||||
|
PrintStack(t.formatPath, out, stack, ind, offsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintStack(formatPath func(string) string, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
|
||||||
if len(stack) == 0 {
|
if len(stack) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2301,7 +2305,7 @@ func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offs
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
|
fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
|
||||||
fmt.Fprintf(out, "%sat %s:%d\n", s, t.formatPath(stack[i].File), stack[i].Line)
|
fmt.Fprintf(out, "%sat %s:%d\n", s, formatPath(stack[i].File), stack[i].Line)
|
||||||
|
|
||||||
if offsets {
|
if offsets {
|
||||||
fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
|
fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
|
||||||
@ -2315,8 +2319,8 @@ func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offs
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
|
fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
|
||||||
fmt.Fprintf(out, "%sat %s:%d\n", s2, t.formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
|
fmt.Fprintf(out, "%sat %s:%d\n", s2, formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
|
||||||
fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), t.formatPath(d.DeferLoc.File), d.DeferLoc.Line)
|
fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), formatPath(d.DeferLoc.File), d.DeferLoc.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := range stack[i].Arguments {
|
for j := range stack[i].Arguments {
|
||||||
|
@ -100,6 +100,7 @@ func (c *Client) ExpectInitializeResponseAndCapabilities(t *testing.T) *dap.Init
|
|||||||
SupportsConditionalBreakpoints: true,
|
SupportsConditionalBreakpoints: true,
|
||||||
SupportsDelayedStackTraceLoading: true,
|
SupportsDelayedStackTraceLoading: true,
|
||||||
SupportTerminateDebuggee: true,
|
SupportTerminateDebuggee: true,
|
||||||
|
SupportsExceptionInfoRequest: true,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(initResp.Body, wantCapabilities) {
|
if !reflect.DeepEqual(initResp.Body, wantCapabilities) {
|
||||||
t.Errorf("capabilities in initializeResponse: got %+v, want %v", pretty(initResp.Body), pretty(wantCapabilities))
|
t.Errorf("capabilities in initializeResponse: got %+v, want %v", pretty(initResp.Body), pretty(wantCapabilities))
|
||||||
@ -399,8 +400,10 @@ func (c *Client) CompletionsRequest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExceptionInfoRequest sends a 'exceptionInfo' request.
|
// ExceptionInfoRequest sends a 'exceptionInfo' request.
|
||||||
func (c *Client) ExceptionInfoRequest() {
|
func (c *Client) ExceptionInfoRequest(threadID int) {
|
||||||
c.send(&dap.ExceptionInfoRequest{Request: *c.newRequest("exceptionInfo")})
|
request := &dap.ExceptionInfoRequest{Request: *c.newRequest("exceptionInfo")}
|
||||||
|
request.Arguments.ThreadId = threadID
|
||||||
|
c.send(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadedSourcesRequest sends a 'loadedSources' request.
|
// LoadedSourcesRequest sends a 'loadedSources' request.
|
||||||
|
@ -21,7 +21,8 @@ const (
|
|||||||
UnableToListGlobals = 2007
|
UnableToListGlobals = 2007
|
||||||
UnableToLookupVariable = 2008
|
UnableToLookupVariable = 2008
|
||||||
UnableToEvaluateExpression = 2009
|
UnableToEvaluateExpression = 2009
|
||||||
|
UnableToGetExceptionInfo = 2010
|
||||||
|
// Add more codes as we support more requests
|
||||||
DebuggeeIsRunning = 4000
|
DebuggeeIsRunning = 4000
|
||||||
DisconnectError = 5000
|
DisconnectError = 5000
|
||||||
)
|
)
|
||||||
|
@ -10,6 +10,7 @@ package dap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@ -29,6 +30,7 @@ import (
|
|||||||
"github.com/go-delve/delve/pkg/locspec"
|
"github.com/go-delve/delve/pkg/locspec"
|
||||||
"github.com/go-delve/delve/pkg/logflags"
|
"github.com/go-delve/delve/pkg/logflags"
|
||||||
"github.com/go-delve/delve/pkg/proc"
|
"github.com/go-delve/delve/pkg/proc"
|
||||||
|
"github.com/go-delve/delve/pkg/terminal"
|
||||||
"github.com/go-delve/delve/service"
|
"github.com/go-delve/delve/service"
|
||||||
"github.com/go-delve/delve/service/api"
|
"github.com/go-delve/delve/service/api"
|
||||||
"github.com/go-delve/delve/service/debugger"
|
"github.com/go-delve/delve/service/debugger"
|
||||||
@ -102,8 +104,13 @@ type Server struct {
|
|||||||
variableHandles *variablesHandlesMap
|
variableHandles *variablesHandlesMap
|
||||||
// args tracks special settings for handling debug session requests.
|
// args tracks special settings for handling debug session requests.
|
||||||
args launchAttachArgs
|
args launchAttachArgs
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// exceptionErr tracks the runtime error that last occurred.
|
||||||
|
exceptionErr error
|
||||||
|
=======
|
||||||
// clientCapabilities tracks special settings for handling debug session requests.
|
// clientCapabilities tracks special settings for handling debug session requests.
|
||||||
clientCapabilities dapClientCapabilites
|
clientCapabilities dapClientCapabilites
|
||||||
|
>>>>>>> 1e9c5c3b07dc5f0f2b3b1fb17bde6444cbf7ca30
|
||||||
|
|
||||||
// mu synchronizes access to objects set on start-up (from run goroutine)
|
// mu synchronizes access to objects set on start-up (from run goroutine)
|
||||||
// and stopped on teardown (from main goroutine)
|
// and stopped on teardown (from main goroutine)
|
||||||
@ -188,6 +195,7 @@ func NewServer(config *service.Config) *Server {
|
|||||||
stackFrameHandles: newHandlesMap(),
|
stackFrameHandles: newHandlesMap(),
|
||||||
variableHandles: newVariablesHandlesMap(),
|
variableHandles: newVariablesHandlesMap(),
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
exceptionErr: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,6 +579,9 @@ func (s *Server) handleRequest(request dap.Message) {
|
|||||||
// Optional (capability ‘supportsCancelRequest’)
|
// Optional (capability ‘supportsCancelRequest’)
|
||||||
// TODO: does this request make sense for delve?
|
// TODO: does this request make sense for delve?
|
||||||
s.onCancelRequest(request)
|
s.onCancelRequest(request)
|
||||||
|
case *dap.ExceptionInfoRequest:
|
||||||
|
// Optional (capability ‘supportsExceptionInfoRequest’)
|
||||||
|
s.onExceptionInfoRequest(request)
|
||||||
//--- Requests that we do not plan to support ---
|
//--- Requests that we do not plan to support ---
|
||||||
case *dap.RestartFrameRequest:
|
case *dap.RestartFrameRequest:
|
||||||
// Optional (capability ’supportsRestartFrame’)
|
// Optional (capability ’supportsRestartFrame’)
|
||||||
@ -595,10 +606,6 @@ func (s *Server) handleRequest(request dap.Message) {
|
|||||||
case *dap.CompletionsRequest:
|
case *dap.CompletionsRequest:
|
||||||
// Optional (capability ‘supportsCompletionsRequest’)
|
// Optional (capability ‘supportsCompletionsRequest’)
|
||||||
s.sendUnsupportedErrorResponse(request.Request)
|
s.sendUnsupportedErrorResponse(request.Request)
|
||||||
case *dap.ExceptionInfoRequest:
|
|
||||||
// Optional (capability ‘supportsExceptionInfoRequest’)
|
|
||||||
// TODO: does this request make sense for delve?
|
|
||||||
s.sendUnsupportedErrorResponse(request.Request)
|
|
||||||
case *dap.DataBreakpointInfoRequest:
|
case *dap.DataBreakpointInfoRequest:
|
||||||
// Optional (capability ‘supportsDataBreakpoints’)
|
// Optional (capability ‘supportsDataBreakpoints’)
|
||||||
s.sendUnsupportedErrorResponse(request.Request)
|
s.sendUnsupportedErrorResponse(request.Request)
|
||||||
@ -664,6 +671,7 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
|
|||||||
response.Body.SupportsConditionalBreakpoints = true
|
response.Body.SupportsConditionalBreakpoints = true
|
||||||
response.Body.SupportsDelayedStackTraceLoading = true
|
response.Body.SupportsDelayedStackTraceLoading = true
|
||||||
response.Body.SupportTerminateDebuggee = true
|
response.Body.SupportTerminateDebuggee = true
|
||||||
|
response.Body.SupportsExceptionInfoRequest = true
|
||||||
// TODO(polina): support this to match vscode-go functionality
|
// TODO(polina): support this to match vscode-go functionality
|
||||||
response.Body.SupportsSetVariable = false
|
response.Body.SupportsSetVariable = false
|
||||||
// TODO(polina): support these requests in addition to vscode-go feature parity
|
// TODO(polina): support these requests in addition to vscode-go feature parity
|
||||||
@ -1161,6 +1169,7 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
|
|||||||
threads[i].Id = g.ID
|
threads[i].Id = g.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &dap.ThreadsResponse{
|
response := &dap.ThreadsResponse{
|
||||||
Response: *newResponse(request.Request),
|
Response: *newResponse(request.Request),
|
||||||
Body: dap.ThreadsResponseBody{Threads: threads},
|
Body: dap.ThreadsResponseBody{Threads: threads},
|
||||||
@ -1913,6 +1922,81 @@ func (s *Server) onCancelRequest(request *dap.CancelRequest) {
|
|||||||
s.sendNotYetImplementedErrorResponse(request.Request)
|
s.sendNotYetImplementedErrorResponse(request.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onExceptionInfoRequest handles 'exceptionInfo' requests.
|
||||||
|
// Capability 'supportsExceptionInfoRequest' is set in 'initialize' response.
|
||||||
|
func (s *Server) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) {
|
||||||
|
goroutineID := request.Arguments.ThreadId
|
||||||
|
var body dap.ExceptionInfoResponseBody
|
||||||
|
// Get the goroutine and the current state.
|
||||||
|
g, err := s.debugger.FindGoroutine(goroutineID)
|
||||||
|
if err != nil {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if g == nil {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", fmt.Sprintf("could not find goroutine %d", goroutineID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var bpState *proc.BreakpointState
|
||||||
|
if g.Thread != nil {
|
||||||
|
bpState = g.Thread.Breakpoint()
|
||||||
|
}
|
||||||
|
// Check if this goroutine ID is stopped at a breakpoint.
|
||||||
|
if bpState != nil && bpState.Breakpoint != nil && (bpState.Breakpoint.Name == proc.FatalThrow || bpState.Breakpoint.Name == proc.UnrecoveredPanic) {
|
||||||
|
switch bpState.Breakpoint.Name {
|
||||||
|
case proc.FatalThrow:
|
||||||
|
// TODO(suzmue): add the fatal throw reason to body.Description.
|
||||||
|
body.ExceptionId = "fatal error"
|
||||||
|
case proc.UnrecoveredPanic:
|
||||||
|
body.ExceptionId = "panic"
|
||||||
|
// Attempt to get the value of the panic message.
|
||||||
|
exprVar, err := s.debugger.EvalVariableInScope(goroutineID, 0, 0, "(*msgs).arg.(data)", DefaultLoadConfig)
|
||||||
|
if err == nil {
|
||||||
|
body.Description = exprVar.Value.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If this thread is not stopped on a breakpoint, then a runtime error must have occurred.
|
||||||
|
// If we do not have any error saved, or if this thread is not current thread,
|
||||||
|
// return an error.
|
||||||
|
if s.exceptionErr == nil {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", "no runtime error found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := s.debugger.State( /*nowait*/ true)
|
||||||
|
if err != nil {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if state == nil || state.CurrentThread == nil || g.Thread == nil || state.CurrentThread.ID != g.Thread.ThreadID() {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", fmt.Sprintf("no exception found for goroutine %d", goroutineID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body.ExceptionId = "runtime error"
|
||||||
|
body.Description = s.exceptionErr.Error()
|
||||||
|
if body.Description == "bad access" {
|
||||||
|
body.Description = BetterBadAccessError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frames, err := s.debugger.Stacktrace(goroutineID, s.args.stackTraceDepth, 0)
|
||||||
|
if err == nil && len(frames) > 0 {
|
||||||
|
apiFrames, err := s.debugger.ConvertStacktrace(frames, nil)
|
||||||
|
if err == nil {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprintln(&buf, "Stack:")
|
||||||
|
terminal.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false)
|
||||||
|
body.Details.StackTrace = buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response := &dap.ExceptionInfoResponse{
|
||||||
|
Response: *newResponse(request.Request),
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
s.send(response)
|
||||||
|
}
|
||||||
|
|
||||||
// sendErrorResponseWithOpts offers configuration options.
|
// sendErrorResponseWithOpts offers configuration options.
|
||||||
// showUser - if true, the error will be shown to the user (e.g. via a visible pop-up)
|
// showUser - if true, the error will be shown to the user (e.g. via a visible pop-up)
|
||||||
func (s *Server) sendErrorResponseWithOpts(request dap.Request, id int, summary, details string, showUser bool) {
|
func (s *Server) sendErrorResponseWithOpts(request dap.Request, id int, summary, details string, showUser bool) {
|
||||||
@ -1987,6 +2071,7 @@ Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https
|
|||||||
func (s *Server) resetHandlesForStoppedEvent() {
|
func (s *Server) resetHandlesForStoppedEvent() {
|
||||||
s.stackFrameHandles.reset()
|
s.stackFrameHandles.reset()
|
||||||
s.variableHandles.reset()
|
s.variableHandles.reset()
|
||||||
|
s.exceptionErr = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// doRunCommand runs a debugger command until it stops on
|
// doRunCommand runs a debugger command until it stops on
|
||||||
@ -2014,6 +2099,9 @@ func (s *Server) doRunCommand(command string, asyncSetupDone chan struct{}) {
|
|||||||
stopped.Body.AllThreadsStopped = true
|
stopped.Body.AllThreadsStopped = true
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// TODO(suzmue): If stopped.Body.ThreadId is not a valid goroutine
|
||||||
|
// then the stopped reason does not show up anywhere in the
|
||||||
|
// vscode ui.
|
||||||
stopped.Body.ThreadId = stoppedGoroutineID(state)
|
stopped.Body.ThreadId = stoppedGoroutineID(state)
|
||||||
|
|
||||||
switch stopReason {
|
switch stopReason {
|
||||||
@ -2031,14 +2119,18 @@ func (s *Server) doRunCommand(command string, asyncSetupDone chan struct{}) {
|
|||||||
if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
|
if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
|
||||||
switch state.CurrentThread.Breakpoint.Name {
|
switch state.CurrentThread.Breakpoint.Name {
|
||||||
case proc.FatalThrow:
|
case proc.FatalThrow:
|
||||||
stopped.Body.Reason = "fatal error"
|
stopped.Body.Reason = "exception"
|
||||||
|
stopped.Body.Description = "Paused on fatal error"
|
||||||
case proc.UnrecoveredPanic:
|
case proc.UnrecoveredPanic:
|
||||||
stopped.Body.Reason = "panic"
|
stopped.Body.Reason = "exception"
|
||||||
|
stopped.Body.Description = "Paused on panic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
s.exceptionErr = err
|
||||||
s.log.Error("runtime error: ", err)
|
s.log.Error("runtime error: ", err)
|
||||||
stopped.Body.Reason = "runtime error"
|
stopped.Body.Reason = "exception"
|
||||||
|
stopped.Body.Description = "Paused on runtime error"
|
||||||
stopped.Body.Text = err.Error()
|
stopped.Body.Text = err.Error()
|
||||||
// Special case in the spirit of https://github.com/microsoft/vscode-go/issues/1903
|
// Special case in the spirit of https://github.com/microsoft/vscode-go/issues/1903
|
||||||
if stopped.Body.Text == "bad access" {
|
if stopped.Body.Text == "bad access" {
|
||||||
@ -2048,19 +2140,6 @@ func (s *Server) doRunCommand(command string, asyncSetupDone chan struct{}) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
stopped.Body.ThreadId = stoppedGoroutineID(state)
|
stopped.Body.ThreadId = stoppedGoroutineID(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(polina): according to the spec, the extra 'text' is supposed to show up in the UI (e.g. on hover),
|
|
||||||
// but so far I am unable to get this to work in vscode - see https://github.com/microsoft/vscode/issues/104475.
|
|
||||||
// Options to explore:
|
|
||||||
// - supporting ExceptionInfo request
|
|
||||||
// - virtual variable scope for Exception that shows the message (details here: https://github.com/microsoft/vscode/issues/3101)
|
|
||||||
// In the meantime, provide the extra details by outputing an error message.
|
|
||||||
s.send(&dap.OutputEvent{
|
|
||||||
Event: *newEvent("output"),
|
|
||||||
Body: dap.OutputEventBody{
|
|
||||||
Output: fmt.Sprintf("ERROR: %s\n", stopped.Body.Text),
|
|
||||||
Category: "stderr",
|
|
||||||
}})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: If we happen to be responding to another request with an is-running
|
// NOTE: If we happen to be responding to another request with an is-running
|
||||||
|
@ -2698,14 +2698,16 @@ func TestBadAccess(t *testing.T) {
|
|||||||
|
|
||||||
expectStoppedOnError := func(errorPrefix string) {
|
expectStoppedOnError := func(errorPrefix string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
oe := client.ExpectOutputEvent(t)
|
|
||||||
if oe.Body.Category != "stderr" || !strings.HasPrefix(oe.Body.Output, "ERROR: "+errorPrefix) {
|
|
||||||
t.Errorf("\ngot %#v\nwant Category=\"stderr\" Output=\"%s ...\"", oe, errorPrefix)
|
|
||||||
}
|
|
||||||
se := client.ExpectStoppedEvent(t)
|
se := client.ExpectStoppedEvent(t)
|
||||||
if se.Body.ThreadId != 1 || se.Body.Reason != "runtime error" || !strings.HasPrefix(se.Body.Text, errorPrefix) {
|
if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "Paused on runtime error" || !strings.HasPrefix(se.Body.Text, errorPrefix) {
|
||||||
t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"runtime error\" Text=\"%s\"", se, errorPrefix)
|
t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"Paused on runtime error\" Text=\"%s\"", se, errorPrefix)
|
||||||
}
|
}
|
||||||
|
client.ExceptionInfoRequest(1)
|
||||||
|
eInfo := client.ExpectExceptionInfoResponse(t)
|
||||||
|
if eInfo.Body.ExceptionId != "runtime error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) {
|
||||||
|
t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=\"%s\"", eInfo, errorPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.ContinueRequest(1)
|
client.ContinueRequest(1)
|
||||||
@ -2750,8 +2752,14 @@ func TestPanicBreakpointOnContinue(t *testing.T) {
|
|||||||
client.ExpectContinueResponse(t)
|
client.ExpectContinueResponse(t)
|
||||||
|
|
||||||
se := client.ExpectStoppedEvent(t)
|
se := client.ExpectStoppedEvent(t)
|
||||||
if se.Body.ThreadId != 1 || se.Body.Reason != "panic" {
|
if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "Paused on panic" {
|
||||||
t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"panic\"", se)
|
t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"Paused on panic\"", se)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.ExceptionInfoRequest(1)
|
||||||
|
eInfo := client.ExpectExceptionInfoResponse(t)
|
||||||
|
if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != "\"BOOM!\"" {
|
||||||
|
t.Errorf("\ngot %#v\nwant ExceptionId=\"panic\" Description=\"\"BOOM!\"\"", eInfo)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
@ -2782,9 +2790,14 @@ func TestPanicBreakpointOnNext(t *testing.T) {
|
|||||||
client.ExpectNextResponse(t)
|
client.ExpectNextResponse(t)
|
||||||
|
|
||||||
se := client.ExpectStoppedEvent(t)
|
se := client.ExpectStoppedEvent(t)
|
||||||
|
if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "Paused on panic" {
|
||||||
|
t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"Paused on panic\"", se)
|
||||||
|
}
|
||||||
|
|
||||||
if se.Body.ThreadId != 1 || se.Body.Reason != "panic" {
|
client.ExceptionInfoRequest(1)
|
||||||
t.Errorf("\ngot %#v\nexpected ThreadId=1 Reason=\"panic\"", se)
|
eInfo := client.ExpectExceptionInfoResponse(t)
|
||||||
|
if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != "\"BOOM!\"" {
|
||||||
|
t.Errorf("\ngot %#v\nwant ExceptionId=\"panic\" Description=\"\"BOOM!\"\"", eInfo)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
@ -2809,8 +2822,8 @@ func TestFatalThrowBreakpoint(t *testing.T) {
|
|||||||
client.ExpectContinueResponse(t)
|
client.ExpectContinueResponse(t)
|
||||||
|
|
||||||
se := client.ExpectStoppedEvent(t)
|
se := client.ExpectStoppedEvent(t)
|
||||||
if se.Body.Reason != "fatal error" {
|
if se.Body.Reason != "exception" || se.Body.Description != "Paused on fatal error" {
|
||||||
t.Errorf("\ngot %#v\nwant Reason=\"fatal error\"", se)
|
t.Errorf("\ngot %#v\nwant Reason=\"exception\" Description=\"Paused on fatal error\"", se)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
@ -3171,9 +3184,6 @@ func TestUnupportedCommandResponses(t *testing.T) {
|
|||||||
client.CompletionsRequest()
|
client.CompletionsRequest()
|
||||||
expectUnsupportedCommand("completions")
|
expectUnsupportedCommand("completions")
|
||||||
|
|
||||||
client.ExceptionInfoRequest()
|
|
||||||
expectUnsupportedCommand("exceptionInfo")
|
|
||||||
|
|
||||||
client.DataBreakpointInfoRequest()
|
client.DataBreakpointInfoRequest()
|
||||||
expectUnsupportedCommand("dataBreakpointInfo")
|
expectUnsupportedCommand("dataBreakpointInfo")
|
||||||
|
|
||||||
|
@ -967,6 +967,14 @@ func (d *Debugger) FindThread(id int) (proc.Thread, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindGoroutine returns the goroutine for the given 'id'.
|
||||||
|
func (d *Debugger) FindGoroutine(id int) (*proc.G, error) {
|
||||||
|
d.targetMutex.Lock()
|
||||||
|
defer d.targetMutex.Unlock()
|
||||||
|
|
||||||
|
return proc.FindGoroutine(d.target, id)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Debugger) setRunning(running bool) {
|
func (d *Debugger) setRunning(running bool) {
|
||||||
d.runningMutex.Lock()
|
d.runningMutex.Lock()
|
||||||
d.running = running
|
d.running = running
|
||||||
|
Loading…
Reference in New Issue
Block a user