From 060bdd8d39e5f7b2c44f71bcd1f9066017ecf433 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Jul 2024 14:58:36 +0200 Subject: [PATCH] Added Debug gRPC adapter function --- commands/service_debug.go | 68 +++++++++++++++++++++++++++++++++++-- internal/cli/debug/debug.go | 5 ++- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/commands/service_debug.go b/commands/service_debug.go index 7ee076a08e3..656c3f6ec11 100644 --- a/commands/service_debug.go +++ b/commands/service_debug.go @@ -16,11 +16,14 @@ package commands import ( + "context" "errors" "os" + "sync/atomic" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "google.golang.org/grpc/metadata" "fmt" "io" @@ -32,9 +35,68 @@ import ( "github.com/sirupsen/logrus" ) -// Debug returns a stream response that can be used to fetch data from the -// target. The first message passed through the `Debug` request must -// contain DebugRequest configuration params, not data. +type debugServer struct { + ctx context.Context + req atomic.Pointer[rpc.GetDebugConfigRequest] + in io.Reader + out io.Writer + resultCB func(*rpc.DebugResponse_Result) +} + +func (s *debugServer) Send(resp *rpc.DebugResponse) error { + if len(resp.GetData()) > 0 { + if _, err := s.out.Write(resp.GetData()); err != nil { + return err + } + } + if res := resp.GetResult(); res != nil { + s.resultCB(res) + } + return nil +} + +func (s *debugServer) Recv() (r *rpc.DebugRequest, e error) { + if conf := s.req.Swap(nil); conf != nil { + return &rpc.DebugRequest{DebugRequest: s.req.Load()}, nil + } + buff := make([]byte, 4096) + n, err := s.in.Read(buff) + if err != nil { + return nil, err + } + return &rpc.DebugRequest{Data: buff[:n]}, nil +} + +func (s *debugServer) Context() context.Context { return s.ctx } +func (s *debugServer) RecvMsg(m any) error { return nil } +func (s *debugServer) SendHeader(metadata.MD) error { return nil } +func (s *debugServer) SendMsg(m any) error { return nil } +func (s *debugServer) SetHeader(metadata.MD) error { return nil } +func (s *debugServer) SetTrailer(metadata.MD) {} + +// DebugServerToStreams creates a debug server that proxies the data to the given io streams. +// The GetDebugConfigRequest is used to configure the debbuger. sig is a channel that can be +// used to send os.Interrupt to the debug process. resultCB is a callback function that will +// receive the Debug result. +func DebugServerToStreams( + ctx context.Context, + req *rpc.GetDebugConfigRequest, + in io.Reader, out io.Writer, + sig chan os.Signal, + resultCB func(*rpc.DebugResponse_Result), +) rpc.ArduinoCoreService_DebugServer { + server := &debugServer{ + ctx: ctx, + in: in, + out: out, + resultCB: resultCB, + } + server.req.Store(req) + return server +} + +// Debug starts a debugging session. The first message passed through the `Debug` request must +// contain DebugRequest configuration params and no data. func (s *arduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer) error { // Utility functions syncSend := NewSynchronizedSend(stream.Send) diff --git a/internal/cli/debug/debug.go b/internal/cli/debug/debug.go index c83a650842d..84ac83c8cad 100644 --- a/internal/cli/debug/debug.go +++ b/internal/cli/debug/debug.go @@ -142,7 +142,10 @@ func runDebugCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args if err != nil { feedback.FatalError(err, feedback.ErrBadArgument) } - if _, err := commands.Debug(ctx, debugConfigRequested, in, out, ctrlc); err != nil { + + resultCB := func(dr *rpc.DebugResponse_Result) {} + stream := commands.DebugServerToStreams(ctx, debugConfigRequested, in, out, ctrlc, resultCB) + if err := srv.Debug(stream); err != nil { errcode := feedback.ErrGeneric if errors.Is(err, &cmderrors.MissingProgrammerError{}) { errcode = feedback.ErrMissingProgrammer