595 lines
21 KiB
Go
595 lines
21 KiB
Go
// Copyright 2020 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// This file defines helpers and request handlers for a dummy server
|
|
// that accepts DAP requests and responds with dummy or error responses.
|
|
// Fake-supported requests:
|
|
// - initialize
|
|
// - launch
|
|
// - setBreakpoints
|
|
// - setExceptionBreakpoints
|
|
// - configurationDone
|
|
// - threads
|
|
// - stackTrace
|
|
// - scopes
|
|
// - variables
|
|
// - continue
|
|
// - disconnect
|
|
// All other requests result in ErrorResponse's.
|
|
//
|
|
// The server uses the following goroutines:
|
|
// - "main" goroutine accepts client connections one by one and
|
|
// handles them serially by reading and decoding incoming requests
|
|
// and dispatching each one to a new goroutine for further
|
|
// processing.
|
|
// - per-request goroutines process each request as if
|
|
// letting fake debugger take over. They send events and responses
|
|
// via the sender goroutine.
|
|
// - sender goroutine listens for messages to send and
|
|
// writes them to the client connection.
|
|
//
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/google/go-dap"
|
|
)
|
|
|
|
// server starts a server that listens on a specified port
|
|
// and blocks indefinitely. This server can accept multiple
|
|
// client connections at the same time.
|
|
func server(port string) error {
|
|
listener, err := net.Listen("tcp", ":"+port)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer listener.Close()
|
|
log.Println("Started server at", listener.Addr())
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
log.Println("Connection failed:", err)
|
|
continue
|
|
}
|
|
log.Println("Accepted connection from", conn.RemoteAddr())
|
|
// Handle multiple client connections concurrently
|
|
go handleConnection(conn)
|
|
}
|
|
}
|
|
|
|
// handleConnection handles a connection from a single client.
|
|
// It reads and decodes the incoming data and dispatches it
|
|
// to per-request processing goroutines. It also launches the
|
|
// sender goroutine to send resulting messages over the connection
|
|
// back to the client.
|
|
func handleConnection(conn net.Conn) {
|
|
debugSession := fakeDebugSession{
|
|
rw: bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)),
|
|
sendQueue: make(chan dap.Message),
|
|
stopDebug: make(chan struct{}),
|
|
}
|
|
go debugSession.sendFromQueue()
|
|
|
|
for {
|
|
err := debugSession.handleRequest()
|
|
// TODO(polina): check for connection vs decoding error?
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
log.Println("No more data to read:", err)
|
|
break
|
|
}
|
|
// There maybe more messages to process, but
|
|
// we will start with the strict behavior of only accepting
|
|
// expected inputs.
|
|
log.Fatal("Server error: ", err)
|
|
}
|
|
}
|
|
|
|
log.Println("Closing connection from", conn.RemoteAddr())
|
|
close(debugSession.stopDebug)
|
|
debugSession.sendWg.Wait()
|
|
close(debugSession.sendQueue)
|
|
conn.Close()
|
|
}
|
|
|
|
func (ds *fakeDebugSession) handleRequest() error {
|
|
log.Println("Reading request...")
|
|
request, err := dap.ReadProtocolMessage(ds.rw.Reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("Received request\n\t%#v\n", request)
|
|
ds.sendWg.Add(1)
|
|
go func() {
|
|
ds.dispatchRequest(request)
|
|
ds.sendWg.Done()
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// dispatchRequest launches a new goroutine to process each request
|
|
// and send back events and responses.
|
|
func (ds *fakeDebugSession) dispatchRequest(request dap.Message) {
|
|
switch request := request.(type) {
|
|
case *dap.InitializeRequest:
|
|
ds.onInitializeRequest(request)
|
|
case *dap.LaunchRequest:
|
|
ds.onLaunchRequest(request)
|
|
case *dap.AttachRequest:
|
|
ds.onAttachRequest(request)
|
|
case *dap.DisconnectRequest:
|
|
ds.onDisconnectRequest(request)
|
|
case *dap.TerminateRequest:
|
|
ds.onTerminateRequest(request)
|
|
case *dap.RestartRequest:
|
|
ds.onRestartRequest(request)
|
|
case *dap.SetBreakpointsRequest:
|
|
ds.onSetBreakpointsRequest(request)
|
|
case *dap.SetFunctionBreakpointsRequest:
|
|
ds.onSetFunctionBreakpointsRequest(request)
|
|
case *dap.SetExceptionBreakpointsRequest:
|
|
ds.onSetExceptionBreakpointsRequest(request)
|
|
case *dap.ConfigurationDoneRequest:
|
|
ds.onConfigurationDoneRequest(request)
|
|
case *dap.ContinueRequest:
|
|
ds.onContinueRequest(request)
|
|
case *dap.NextRequest:
|
|
ds.onNextRequest(request)
|
|
case *dap.StepInRequest:
|
|
ds.onStepInRequest(request)
|
|
case *dap.StepOutRequest:
|
|
ds.onStepOutRequest(request)
|
|
case *dap.StepBackRequest:
|
|
ds.onStepBackRequest(request)
|
|
case *dap.ReverseContinueRequest:
|
|
ds.onReverseContinueRequest(request)
|
|
case *dap.RestartFrameRequest:
|
|
ds.onRestartFrameRequest(request)
|
|
case *dap.GotoRequest:
|
|
ds.onGotoRequest(request)
|
|
case *dap.PauseRequest:
|
|
ds.onPauseRequest(request)
|
|
case *dap.StackTraceRequest:
|
|
ds.onStackTraceRequest(request)
|
|
case *dap.ScopesRequest:
|
|
ds.onScopesRequest(request)
|
|
case *dap.VariablesRequest:
|
|
ds.onVariablesRequest(request)
|
|
case *dap.SetVariableRequest:
|
|
ds.onSetVariableRequest(request)
|
|
case *dap.SetExpressionRequest:
|
|
ds.onSetExpressionRequest(request)
|
|
case *dap.SourceRequest:
|
|
ds.onSourceRequest(request)
|
|
case *dap.ThreadsRequest:
|
|
ds.onThreadsRequest(request)
|
|
case *dap.TerminateThreadsRequest:
|
|
ds.onTerminateThreadsRequest(request)
|
|
case *dap.EvaluateRequest:
|
|
ds.onEvaluateRequest(request)
|
|
case *dap.StepInTargetsRequest:
|
|
ds.onStepInTargetsRequest(request)
|
|
case *dap.GotoTargetsRequest:
|
|
ds.onGotoTargetsRequest(request)
|
|
case *dap.CompletionsRequest:
|
|
ds.onCompletionsRequest(request)
|
|
case *dap.ExceptionInfoRequest:
|
|
ds.onExceptionInfoRequest(request)
|
|
case *dap.LoadedSourcesRequest:
|
|
ds.onLoadedSourcesRequest(request)
|
|
case *dap.DataBreakpointInfoRequest:
|
|
ds.onDataBreakpointInfoRequest(request)
|
|
case *dap.SetDataBreakpointsRequest:
|
|
ds.onSetDataBreakpointsRequest(request)
|
|
case *dap.ReadMemoryRequest:
|
|
ds.onReadMemoryRequest(request)
|
|
case *dap.DisassembleRequest:
|
|
ds.onDisassembleRequest(request)
|
|
case *dap.CancelRequest:
|
|
ds.onCancelRequest(request)
|
|
case *dap.BreakpointLocationsRequest:
|
|
ds.onBreakpointLocationsRequest(request)
|
|
default:
|
|
log.Fatalf("Unable to process %#v", request)
|
|
}
|
|
}
|
|
|
|
// send lets the sender goroutine know via a channel that there is
|
|
// a message to be sent to client. This is called by per-request
|
|
// goroutines to send events and responses for each request and
|
|
// to notify of events triggered by the fake debugger.
|
|
func (ds *fakeDebugSession) send(message dap.Message) {
|
|
ds.sendQueue <- message
|
|
}
|
|
|
|
// sendFromQueue is to be run in a separate goroutine to listen on a
|
|
// channel for messages to send back to the client. It will
|
|
// return once the channel is closed.
|
|
func (ds *fakeDebugSession) sendFromQueue() {
|
|
for message := range ds.sendQueue {
|
|
dap.WriteProtocolMessage(ds.rw.Writer, message)
|
|
log.Printf("Message sent\n\t%#v\n", message)
|
|
ds.rw.Flush()
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Very Fake Debugger
|
|
//
|
|
|
|
// The debugging session will keep track of how many breakpoints
|
|
// have been set. Once start-up is done (i.e. configurationDone
|
|
// request is processed), it will "stop" at each breakpoint one by
|
|
// one, and once there are no more, it will trigger a terminated event.
|
|
type fakeDebugSession struct {
|
|
// rw is used to read requests and write events/responses
|
|
rw *bufio.ReadWriter
|
|
|
|
// sendQueue is used to capture messages from multiple request
|
|
// processing goroutines while writing them to the client connection
|
|
// from a single goroutine via sendFromQueue. We must keep track of
|
|
// the multiple channel senders with a wait group to make sure we do
|
|
// not close this channel prematurely. Closing this channel will signal
|
|
// the sendFromQueue goroutine that it can exit.
|
|
sendQueue chan dap.Message
|
|
sendWg sync.WaitGroup
|
|
|
|
// stopDebug is used to notify long-running handlers to stop processing.
|
|
stopDebug chan struct{}
|
|
|
|
// bpSet is a counter of the remaining breakpoints that the debug
|
|
// session is yet to stop at before the program terminates.
|
|
bpSet int
|
|
bpSetMux sync.Mutex
|
|
}
|
|
|
|
// doContinue allows fake program execution to continue when the program
|
|
// is started or unpaused. It simulates events from the debug session
|
|
// by "stopping" on a breakpoint or terminating if there are no more
|
|
// breakpoints. Safe to use concurrently.
|
|
func (ds *fakeDebugSession) doContinue() {
|
|
var e dap.Message
|
|
ds.bpSetMux.Lock()
|
|
if ds.bpSet == 0 {
|
|
// Pretend that the program is running.
|
|
// The delay will allow for all in-flight responses
|
|
// to be sent before termination.
|
|
time.Sleep(1000 * time.Millisecond)
|
|
e = &dap.TerminatedEvent{
|
|
Event: *newEvent("terminated"),
|
|
}
|
|
} else {
|
|
e = &dap.StoppedEvent{
|
|
Event: *newEvent("stopped"),
|
|
Body: dap.StoppedEventBody{Reason: "breakpoint", ThreadId: 1, AllThreadsStopped: true},
|
|
}
|
|
ds.bpSet--
|
|
}
|
|
ds.bpSetMux.Unlock()
|
|
ds.send(e)
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Request Handlers
|
|
//
|
|
// Below is a dummy implementation of the request handlers.
|
|
// They take no action, but just return dummy responses.
|
|
// A real debug adaptor would call the debugger methods here
|
|
// and use their results to populate each response.
|
|
|
|
func (ds *fakeDebugSession) onInitializeRequest(request *dap.InitializeRequest) {
|
|
response := &dap.InitializeResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body.SupportsConfigurationDoneRequest = true
|
|
response.Body.SupportsFunctionBreakpoints = false
|
|
response.Body.SupportsConditionalBreakpoints = false
|
|
response.Body.SupportsHitConditionalBreakpoints = false
|
|
response.Body.SupportsEvaluateForHovers = false
|
|
response.Body.ExceptionBreakpointFilters = []dap.ExceptionBreakpointsFilter{}
|
|
response.Body.SupportsStepBack = false
|
|
response.Body.SupportsSetVariable = false
|
|
response.Body.SupportsRestartFrame = false
|
|
response.Body.SupportsGotoTargetsRequest = false
|
|
response.Body.SupportsStepInTargetsRequest = false
|
|
response.Body.SupportsCompletionsRequest = false
|
|
response.Body.CompletionTriggerCharacters = []string{}
|
|
response.Body.SupportsModulesRequest = false
|
|
response.Body.AdditionalModuleColumns = []dap.ColumnDescriptor{}
|
|
response.Body.SupportedChecksumAlgorithms = []dap.ChecksumAlgorithm{}
|
|
response.Body.SupportsRestartRequest = false
|
|
response.Body.SupportsExceptionOptions = false
|
|
response.Body.SupportsValueFormattingOptions = false
|
|
response.Body.SupportsExceptionInfoRequest = false
|
|
response.Body.SupportTerminateDebuggee = false
|
|
response.Body.SupportsDelayedStackTraceLoading = false
|
|
response.Body.SupportsLoadedSourcesRequest = false
|
|
response.Body.SupportsLogPoints = false
|
|
response.Body.SupportsTerminateThreadsRequest = false
|
|
response.Body.SupportsSetExpression = false
|
|
response.Body.SupportsTerminateRequest = false
|
|
response.Body.SupportsDataBreakpoints = false
|
|
response.Body.SupportsReadMemoryRequest = false
|
|
response.Body.SupportsDisassembleRequest = false
|
|
response.Body.SupportsCancelRequest = false
|
|
response.Body.SupportsBreakpointLocationsRequest = false
|
|
// This is a fake set up, so we can start "accepting" configuration
|
|
// requests for setting breakpoints, etc from the client at any time.
|
|
// Notify the client with an 'initialized' event. The client will end
|
|
// the configuration sequence with 'configurationDone' request.
|
|
e := &dap.InitializedEvent{Event: *newEvent("initialized")}
|
|
ds.send(e)
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onLaunchRequest(request *dap.LaunchRequest) {
|
|
// This is where a real debug adaptor would check the soundness of the
|
|
// arguments (e.g. program from launch.json) and then use them to launch the
|
|
// debugger and attach to the program.
|
|
response := &dap.LaunchResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onAttachRequest(request *dap.AttachRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "AttachRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onDisconnectRequest(request *dap.DisconnectRequest) {
|
|
response := &dap.DisconnectResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onTerminateRequest(request *dap.TerminateRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "TerminateRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onRestartRequest(request *dap.RestartRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "RestartRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
|
|
response := &dap.SetBreakpointsResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body.Breakpoints = make([]dap.Breakpoint, len(request.Arguments.Breakpoints))
|
|
for i, b := range request.Arguments.Breakpoints {
|
|
response.Body.Breakpoints[i].Line = b.Line
|
|
response.Body.Breakpoints[i].Verified = true
|
|
ds.bpSetMux.Lock()
|
|
ds.bpSet++
|
|
ds.bpSetMux.Unlock()
|
|
}
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "SetFunctionBreakpointsRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest) {
|
|
response := &dap.SetExceptionBreakpointsResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest) {
|
|
// This would be the place to check if the session was configured to
|
|
// stop on entry and if that is the case, to issue a
|
|
// stopped-on-breakpoint event. This being a mock implementation,
|
|
// we "let" the program continue after sending a successful response.
|
|
e := &dap.ThreadEvent{Event: *newEvent("thread"), Body: dap.ThreadEventBody{Reason: "started", ThreadId: 1}}
|
|
ds.send(e)
|
|
response := &dap.ConfigurationDoneResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
ds.send(response)
|
|
ds.doContinue()
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onContinueRequest(request *dap.ContinueRequest) {
|
|
response := &dap.ContinueResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
ds.send(response)
|
|
ds.doContinue()
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onNextRequest(request *dap.NextRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "NextRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onStepInRequest(request *dap.StepInRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "StepInRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onStepOutRequest(request *dap.StepOutRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "StepOutRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onStepBackRequest(request *dap.StepBackRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "StepBackRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onReverseContinueRequest(request *dap.ReverseContinueRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "ReverseContinueRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onRestartFrameRequest(request *dap.RestartFrameRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "RestartFrameRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onGotoRequest(request *dap.GotoRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "GotoRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onPauseRequest(request *dap.PauseRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "PauseRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onStackTraceRequest(request *dap.StackTraceRequest) {
|
|
response := &dap.StackTraceResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body = dap.StackTraceResponseBody{
|
|
StackFrames: []dap.StackFrame{
|
|
{
|
|
Id: 1000,
|
|
Source: &dap.Source{Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go", SourceReference: 0},
|
|
Line: 5,
|
|
Column: 0,
|
|
Name: "main.main",
|
|
},
|
|
},
|
|
TotalFrames: 1,
|
|
}
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onScopesRequest(request *dap.ScopesRequest) {
|
|
response := &dap.ScopesResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body = dap.ScopesResponseBody{
|
|
Scopes: []dap.Scope{
|
|
{Name: "Local", VariablesReference: 1000, Expensive: false},
|
|
{Name: "Global", VariablesReference: 1001, Expensive: true},
|
|
},
|
|
}
|
|
ds.send(response)
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onVariablesRequest(request *dap.VariablesRequest) {
|
|
select {
|
|
case <-ds.stopDebug:
|
|
return
|
|
// simulate long-running processing to make this handler
|
|
// respond to this request after the next request is received
|
|
case <-time.After(100 * time.Millisecond):
|
|
response := &dap.VariablesResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body = dap.VariablesResponseBody{
|
|
Variables: []dap.Variable{{Name: "i", Value: "18434528", EvaluateName: "i", VariablesReference: 0}},
|
|
}
|
|
ds.send(response)
|
|
}
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetVariableRequest(request *dap.SetVariableRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "setVariableRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetExpressionRequest(request *dap.SetExpressionRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "SetExpressionRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSourceRequest(request *dap.SourceRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "SourceRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onThreadsRequest(request *dap.ThreadsRequest) {
|
|
response := &dap.ThreadsResponse{}
|
|
response.Response = *newResponse(request.Seq, request.Command)
|
|
response.Body = dap.ThreadsResponseBody{Threads: []dap.Thread{{Id: 1, Name: "main"}}}
|
|
ds.send(response)
|
|
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "TerminateRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onEvaluateRequest(request *dap.EvaluateRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "EvaluateRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onStepInTargetsRequest(request *dap.StepInTargetsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "StepInTargetRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onGotoTargetsRequest(request *dap.GotoTargetsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "GotoTargetRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onCompletionsRequest(request *dap.CompletionsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "CompletionRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "ExceptionRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "LoadedRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "DataBreakpointInfoRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "SetDataBreakpointsRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onReadMemoryRequest(request *dap.ReadMemoryRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "ReadMemoryRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onDisassembleRequest(request *dap.DisassembleRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "DisassembleRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onCancelRequest(request *dap.CancelRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "CancelRequest is not yet supported"))
|
|
}
|
|
|
|
func (ds *fakeDebugSession) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest) {
|
|
ds.send(newErrorResponse(request.Seq, request.Command, "BreakpointLocationsRequest is not yet supported"))
|
|
}
|
|
|
|
func newEvent(event string) *dap.Event {
|
|
return &dap.Event{
|
|
ProtocolMessage: dap.ProtocolMessage{
|
|
Seq: 0,
|
|
Type: "event",
|
|
},
|
|
Event: event,
|
|
}
|
|
}
|
|
|
|
func newResponse(requestSeq int, command string) *dap.Response {
|
|
return &dap.Response{
|
|
ProtocolMessage: dap.ProtocolMessage{
|
|
Seq: 0,
|
|
Type: "response",
|
|
},
|
|
Command: command,
|
|
RequestSeq: requestSeq,
|
|
Success: true,
|
|
}
|
|
}
|
|
|
|
func newErrorResponse(requestSeq int, command string, message string) *dap.ErrorResponse {
|
|
er := &dap.ErrorResponse{}
|
|
er.Response = *newResponse(requestSeq, command)
|
|
er.Success = false
|
|
er.Message = "unsupported"
|
|
er.Body.Error.Format = message
|
|
er.Body.Error.Id = 12345
|
|
return er
|
|
}
|