Skip to content

Commit fc13047

Browse files
Roberto Soracmagliemasci
authored
Implement debug feature (#590)
* Implement first draft of debugger gRPC service * Working stdio streaming * Improved stdio passing via GRPC * Adjusted protoc definitions * Handle errors gracefully * Add recipe calculation to debug command * First implementation of debug * updated client example for testing * Add test for debug recipe generation * Implement debug command * Implement copyStream * Refactor stream helpers * Extract recipe creation from debug command * Cosmetics here and there * Refreshed client example * Replace with utils function * Remove debug leftover * Refreshed client example * Moved debug proto to its package * Removed sketch.json * Apply general cosmetics * Add test binaries * Added test case for windows path flavor * Use path.FromSlash to test debug tool command generation cross platform easily * Avoid pipe leaking via closing readers and writes in case of abnormal termination * Update client example to better catch gdb prompt * Error messages cosmetics * Use errors.Wrap instead of fmt.Errorf Co-Authored-By: Massimiliano Pippi <[email protected]> * Use errors.Wrap instead of fmt.Errorf Co-Authored-By: Massimiliano Pippi <[email protected]> Co-authored-by: Cristian Maglie <[email protected]> Co-authored-by: Massimiliano Pippi <[email protected]>
1 parent 1eefe49 commit fc13047

File tree

26 files changed

+1449
-173
lines changed

26 files changed

+1449
-173
lines changed

Diff for: Taskfile.yml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ tasks:
77
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/commands/*.proto'
88
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/monitor/*.proto'
99
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/settings/*.proto'
10+
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/debug/*.proto'
1011

1112
build:
1213
desc: Build the project

Diff for: arduino/cores/packagemanager/package_manager.go

-1
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,6 @@ func (tr *ToolReleaseActions) Get() (*cores.ToolRelease, error) {
323323

324324
// GetInstalledPlatformRelease returns the PlatformRelease installed (it is chosen)
325325
func (pm *PackageManager) GetInstalledPlatformRelease(platform *cores.Platform) *cores.PlatformRelease {
326-
pm.Log.Infof("Selecting installed platform release for %s", platform)
327326
releases := platform.GetAllInstalled()
328327
if len(releases) == 0 {
329328
return nil

Diff for: arduino/utils/stream.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package utils
17+
18+
import "io"
19+
20+
// FeedStreamTo creates a pipe to pass data to the writer function.
21+
// FeedStreamTo returns the io.Writer side of the pipe, on which the user can write data
22+
func FeedStreamTo(writer func(data []byte)) io.Writer {
23+
r, w := io.Pipe()
24+
go func() {
25+
data := make([]byte, 1024)
26+
for {
27+
if n, err := r.Read(data); err == nil {
28+
writer(data[:n])
29+
} else {
30+
r.Close()
31+
return
32+
}
33+
}
34+
}()
35+
return w
36+
}
37+
38+
// ConsumeStreamFrom creates a pipe to consume data from the reader function.
39+
// ConsumeStreamFrom returns the io.Reader side of the pipe, which the user can use to consume the data
40+
func ConsumeStreamFrom(reader func() ([]byte, error)) io.Reader {
41+
r, w := io.Pipe()
42+
go func() {
43+
for {
44+
if data, err := reader(); err != nil {
45+
if err == io.EOF {
46+
w.Close()
47+
} else {
48+
w.CloseWithError(err)
49+
}
50+
return
51+
} else if _, err := w.Write(data); err != nil {
52+
w.Close()
53+
return
54+
}
55+
}
56+
}()
57+
return r
58+
}

Diff for: cli/cli.go

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/arduino/arduino-cli/cli/config"
2929
"github.com/arduino/arduino-cli/cli/core"
3030
"github.com/arduino/arduino-cli/cli/daemon"
31+
"github.com/arduino/arduino-cli/cli/debug"
3132
"github.com/arduino/arduino-cli/cli/errorcodes"
3233
"github.com/arduino/arduino-cli/cli/feedback"
3334
"github.com/arduino/arduino-cli/cli/generatedocs"
@@ -77,6 +78,7 @@ func createCliCommandTree(cmd *cobra.Command) {
7778
cmd.AddCommand(lib.NewCommand())
7879
cmd.AddCommand(sketch.NewCommand())
7980
cmd.AddCommand(upload.NewCommand())
81+
cmd.AddCommand(debug.NewCommand())
8082
cmd.AddCommand(version.NewCommand())
8183

8284
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.")

Diff for: cli/daemon/daemon.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/arduino/arduino-cli/cli/globals"
3232
"github.com/arduino/arduino-cli/commands/daemon"
3333
srv_commands "github.com/arduino/arduino-cli/rpc/commands"
34+
srv_debug "github.com/arduino/arduino-cli/rpc/debug"
3435
srv_monitor "github.com/arduino/arduino-cli/rpc/monitor"
3536
srv_settings "github.com/arduino/arduino-cli/rpc/settings"
3637
"github.com/sirupsen/logrus"
@@ -73,16 +74,19 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
7374
VersionString: globals.VersionInfo.VersionString,
7475
})
7576

76-
// register the monitors service
77+
// Register the monitors service
7778
srv_monitor.RegisterMonitorServer(s, &daemon.MonitorService{})
7879

79-
// register the settings service
80+
// Register the settings service
8081
srv_settings.RegisterSettingsServer(s, &daemon.SettingsService{})
8182

83+
// Register the debug session service
84+
srv_debug.RegisterDebugServer(s, &daemon.DebugService{})
85+
8286
if !daemonize {
8387
// When parent process ends terminate also the daemon
8488
go func() {
85-
// stdin is closed when the controlling parent process ends
89+
// Stdin is closed when the controlling parent process ends
8690
_, _ = io.Copy(ioutil.Discard, os.Stdin)
8791
os.Exit(0)
8892
}()
@@ -115,6 +119,6 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
115119
// This message will show up on the stdout of the daemon process so that gRPC clients know it is time to connect.
116120
logrus.Infof("Daemon is listening on TCP port %s...", port)
117121
if err := s.Serve(lis); err != nil {
118-
logrus.Fatalf("failed to serve: %v", err)
122+
logrus.Fatalf("Failed to serve: %v", err)
119123
}
120124
}

Diff for: cli/debug/debug.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package debug
17+
18+
import (
19+
"context"
20+
"os"
21+
22+
"github.com/arduino/arduino-cli/cli/errorcodes"
23+
"github.com/arduino/arduino-cli/cli/feedback"
24+
"github.com/arduino/arduino-cli/cli/instance"
25+
"github.com/arduino/arduino-cli/commands/debug"
26+
dbg "github.com/arduino/arduino-cli/rpc/debug"
27+
"github.com/arduino/go-paths-helper"
28+
"github.com/sirupsen/logrus"
29+
"github.com/spf13/cobra"
30+
)
31+
32+
var (
33+
fqbn string
34+
port string
35+
verbose bool
36+
verify bool
37+
importFile string
38+
)
39+
40+
// NewCommand created a new `upload` command
41+
func NewCommand() *cobra.Command {
42+
debugCommand := &cobra.Command{
43+
Use: "debug",
44+
Short: "Debug Arduino sketches.",
45+
Long: "Debug Arduino sketches. (this command opens an interactive gdb session)",
46+
Example: " " + os.Args[0] + " debug -b arduino:samd:mkr1000 /home/user/Arduino/MySketch",
47+
Args: cobra.MaximumNArgs(1),
48+
Run: run,
49+
}
50+
51+
debugCommand.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:avr:uno")
52+
debugCommand.Flags().StringVarP(&port, "port", "p", "", "Upload port, e.g.: COM10 or /dev/ttyACM0")
53+
debugCommand.Flags().StringVarP(&importFile, "input", "i", "", "Input file to be uploaded for debug.")
54+
55+
return debugCommand
56+
}
57+
58+
func run(command *cobra.Command, args []string) {
59+
instance, err := instance.CreateInstance()
60+
if err != nil {
61+
feedback.Errorf("Error during Debug: %v", err)
62+
os.Exit(errorcodes.ErrGeneric)
63+
}
64+
65+
var path *paths.Path
66+
if len(args) > 0 {
67+
path = paths.New(args[0])
68+
}
69+
sketchPath := initSketchPath(path)
70+
71+
if _, err := debug.Debug(context.Background(), &dbg.DebugConfigReq{
72+
Instance: &dbg.Instance{Id: instance.GetId()},
73+
Fqbn: fqbn,
74+
SketchPath: sketchPath.String(),
75+
Port: port,
76+
ImportFile: importFile,
77+
}, os.Stdin, os.Stdout); err != nil {
78+
feedback.Errorf("Error during Debug: %v", err)
79+
os.Exit(errorcodes.ErrGeneric)
80+
}
81+
}
82+
83+
// initSketchPath returns the current working directory
84+
func initSketchPath(sketchPath *paths.Path) *paths.Path {
85+
if sketchPath != nil {
86+
return sketchPath
87+
}
88+
89+
wd, err := paths.Getwd()
90+
if err != nil {
91+
feedback.Errorf("Couldn't get current working directory: %v", err)
92+
os.Exit(errorcodes.ErrGeneric)
93+
}
94+
logrus.Infof("Reading sketch from dir: %s", wd)
95+
return wd
96+
}

Diff for: client_example/go.mod

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ module github.com/arduino/arduino-cli/client_example
22

33
go 1.13
44

5+
replace github.com/arduino/arduino-cli => ../
6+
57
require (
68
github.com/arduino/arduino-cli v0.0.0-20200109150215-ffa84fdaab21
7-
github.com/gosuri/uitable v0.0.0-20160404203958-36ee7e946282 // indirect
8-
google.golang.org/grpc v1.23.0
9-
)
9+
google.golang.org/grpc v1.27.0
10+
)

0 commit comments

Comments
 (0)