Skip to content

Commit

Permalink
feat: added more debug options (arduino#2356)
Browse files Browse the repository at this point in the history
* Improved specification of debug configuration

* Added integration test

* Added the possibility to provide cortex-debug custom configs

* Added svd-file debug option

* Added textual print of custom cortex-debug config

* Updated docs

* Apply suggestions from code review

Co-authored-by: Umberto Baldi <34278123+umbynos@users.noreply.github.com>

* Added integration test for programmer selection

---------

Co-authored-by: Umberto Baldi <34278123+umbynos@users.noreply.github.com>
  • Loading branch information
cmaglie and umbynos authored Oct 9, 2023
1 parent f561da0 commit 748fcf6
Show file tree
Hide file tree
Showing 10 changed files with 685 additions and 122 deletions.
9 changes: 7 additions & 2 deletions commands/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,18 @@ func getCommandLine(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer
// Extract path to GDB Server
switch debugInfo.GetServer() {
case "openocd":
var openocdConf rpc.DebugOpenOCDServerConfiguration
if err := debugInfo.ServerConfiguration.UnmarshalTo(&openocdConf); err != nil {
return nil, err
}

serverCmd := fmt.Sprintf(`target extended-remote | "%s"`, debugInfo.ServerPath)

if cfg := debugInfo.ServerConfiguration["scripts_dir"]; cfg != "" {
if cfg := openocdConf.GetScriptsDir(); cfg != "" {
serverCmd += fmt.Sprintf(` -s "%s"`, cfg)
}

if script := debugInfo.ServerConfiguration["script"]; script != "" {
for _, script := range openocdConf.GetScripts() {
serverCmd += fmt.Sprintf(` --file "%s"`, script)
}

Expand Down
81 changes: 79 additions & 2 deletions commands/debug/debug_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package debug

import (
"context"
"encoding/json"
"regexp"
"strings"

"github.com/arduino/arduino-cli/arduino"
Expand All @@ -28,6 +30,7 @@ import (
"github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/types/known/anypb"
)

// GetDebugConfig returns metadata to start debugging with the specified board
Expand Down Expand Up @@ -150,14 +153,88 @@ func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Expl

server := debugProperties.Get("server")
toolchain := debugProperties.Get("toolchain")

var serverConfiguration anypb.Any
switch server {
case "openocd":
openocdProperties := debugProperties.SubTree("server." + server)
scripts := openocdProperties.ExtractSubIndexLists("scripts")
if s := openocdProperties.Get("script"); s != "" {
// backward compatibility
scripts = append(scripts, s)
}
openocdConf := &rpc.DebugOpenOCDServerConfiguration{
Path: openocdProperties.Get("path"),
ScriptsDir: openocdProperties.Get("scripts_dir"),
Scripts: scripts,
}
if err := serverConfiguration.MarshalFrom(openocdConf); err != nil {
return nil, err
}
}

var toolchainConfiguration anypb.Any
switch toolchain {
case "gcc":
gccConf := &rpc.DebugGCCToolchainConfiguration{}
if err := toolchainConfiguration.MarshalFrom(gccConf); err != nil {
return nil, err
}
}

cortexDebugCustomJson := ""
if cortexDebugProps := debugProperties.SubTree("cortex-debug.custom"); cortexDebugProps.Size() > 0 {
cortexDebugCustomJson = convertToJsonMap(cortexDebugProps)
}
return &rpc.GetDebugConfigResponse{
Executable: debugProperties.Get("executable"),
Server: server,
ServerPath: debugProperties.Get("server." + server + ".path"),
ServerConfiguration: debugProperties.SubTree("server." + server).AsMap(),
ServerConfiguration: &serverConfiguration,
SvdFile: debugProperties.Get("svd_file"),
Toolchain: toolchain,
ToolchainPath: debugProperties.Get("toolchain.path"),
ToolchainPrefix: debugProperties.Get("toolchain.prefix"),
ToolchainConfiguration: debugProperties.SubTree("toolchain." + toolchain).AsMap(),
ToolchainConfiguration: &toolchainConfiguration,
CortexDebugCustomJson: cortexDebugCustomJson,
}, nil
}

// Extract a JSON from a given properies.Map and converts key-indexed arrays
// like:
//
// my.indexed.array.0=first
// my.indexed.array.1=second
// my.indexed.array.2=third
//
// into the corresponding JSON arrays.
func convertToJsonMap(in *properties.Map) string {
// XXX: Maybe this method could be a good candidate for propertis.Map?

// Find the values that should be kept as is, and the indexed arrays
// that should be later converted into arrays.
arraysKeys := map[string]bool{}
stringKeys := []string{}
trailingNumberMatcher := regexp.MustCompile(`^(.*)\.[0-9]+$`)
for _, k := range in.Keys() {
match := trailingNumberMatcher.FindAllStringSubmatch(k, -1)
if len(match) > 0 && len(match[0]) > 1 {
arraysKeys[match[0][1]] = true
} else {
stringKeys = append(stringKeys, k)
}
}

// Compose a map that can be later marshaled into JSON keeping
// the arrays where they are expected to be.
res := map[string]any{}
for _, k := range stringKeys {
res[k] = in.Get(k)
}
for k := range arraysKeys {
res[k] = in.ExtractSubIndexLists(k)
}

data, _ := json.MarshalIndent(res, "", " ")
return string(data)
}
37 changes: 37 additions & 0 deletions docs/UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@ Here you can find a list of migration guides to handle breaking changes between

## 0.35.0

### CLI `debug --info` changed JSON output.

The string field `server_configuration.script` is now an array and has been renamed `scripts`, here an example:

```json
{
"executable": "/tmp/arduino/sketches/002050EAA7EFB9A4FC451CDFBC0FA2D3/Blink.ino.elf",
"toolchain": "gcc",
"toolchain_path": "/home/user/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/",
"toolchain_prefix": "arm-none-eabi-",
"server": "openocd",
"server_path": "/home/user/.arduino15/packages/arduino/tools/openocd/0.10.0-arduino7/bin/openocd",
"server_configuration": {
"path": "/home/user/.arduino15/packages/arduino/tools/openocd/0.10.0-arduino7/bin/openocd",
"scripts_dir": "/home/user/.arduino15/packages/arduino/tools/openocd/0.10.0-arduino7/share/openocd/scripts/",
"scripts": [
"/home/user/Workspace/arduino-cli/internal/integrationtest/debug/testdata/hardware/my/samd/variants/arduino:mkr1000/openocd_scripts/arduino_zero.cfg"
]
}
}
```

### gRPC `cc.arduino.cli.commands.v1.GetDebugConfigResponse` message has been changed.

The fields `toolchain_configuration` and `server_configuration` are no more generic `map<string, string>` but they have
changed type to `goog.protobuf.Any`, the concrete type is assigned at runtime based on the value of `toolchain` and
`server` fields respectively.

For the moment:

- only `gcc` is supported for `toolchain`, and the concrete type for `toolchain_configuration` is
`DebugGCCToolchainConfiguration`.
- only `openocd` is supported for `server`, and the concrete type for `server_configuration` is
`DebugOpenOCDServerConfiguration`

More concrete type may be added in the future as more servers/toolchains support is implemented.

### gRPC service `cc.arduino.cli.debug.v1` moved to `cc.arduino.cli.commands.v1`.

The gRPC service `cc.arduino.cli.debug.v1` has been removed and all gRPC messages and rpc calls have been moved to
Expand Down
62 changes: 61 additions & 1 deletion docs/platform-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -1335,13 +1335,73 @@ the [core platform](#platform-terminology) is not used at all in defining the re
actions. When using Arduino development software other than the Arduino IDE, the handling of properties from the core
platform's platform.txt is done as usual.

### Sketch debugging configuration
## Sketch debugging configuration

Starting from Arduino CLI 0.9.0 / Arduino IDE 2.x, sketch debugging support is available for platforms.

The debug action is triggered when the user clicks the Debug button in the Arduino IDE or runs the
[`arduino-cli debug`](commands/arduino-cli_debug.md) command.

Since opening a debug session requires the orchestration of numerous tools, the CLI/IDE will take care of that duty:
differently from the upload actions, there is no need for the platform to provide debug recipes, the only requirement is
to provide some debug configuration directives.

### Debugger configuration directives

All the debug directives are grouped under the `debug.*` directives. Here is the complete list of the supported
directives:

- `debug.toolchain`: is a unique identifier of the required toolchain, currently we support `gcc` (and compatible) only
- `debug.toolchain.path`: is the absolute path to the toolchain directory
- `debug.toolchain.prefix`: is the prefix of the toolchain (for example `arm-none-eabi-`)
- `debug.server`: is a unique identifier of the required debug server, currently we support only `openocd`
- `debug.svd_file`: is the absolute path to the SVD descriptor.

OpenOCD server specific configurations:

- `debug.server.openocd.path`: is the absolute path to the OpenOCD directory
- `debug.server.openocd.scripts_dir`: is the absolute path to the OpenOCD scripts directory
- `debug.server.openocd.scripts.N`: is a list of OpenOCD scripts to run (where N is a number starting from 0)

### Custom config for Cortext-debug plugin for Arduino IDE

The Arduino IDE uses cortex-debug plugin to start a debugging session. The IDE creates a `launch.json` file that is
needed to start the debugging via the cortex-debug plugin. To give the platform developers more flexibility, it is
allowed to pass any extra arbitrary setup to `launch.json` generated by the IDE. To allow this the directives under the
group `debug.cortex-debug.custom.*` are converted into JSON and added to the generated `launch.json` as-is. Moreover, if
a directive has a key with a numeric suffix, it is converted into a JSON array.

For example the following directives:

```
debug.cortex-debug.custom.postAttachCommands.0=set remote hardware-watchpoint-limit 2
debug.cortex-debug.custom.postAttachCommands.1=monitor reset halt
debug.cortex-debug.custom.postAttachCommands.2=monitor gdb_sync
debug.cortex-debug.custom.postAttachCommands.3=thb setup
debug.cortex-debug.custom.postAttachCommands.4=c
debug.cortex-debug.custom.overrideRestartCommands.0=monitor reset halt
debug.cortex-debug.custom.overrideRestartCommands.1=monitor gdb_sync
debug.cortex-debug.custom.overrideRestartCommands.2=thb setup
debug.cortex-debug.custom.overrideRestartCommands.3=c
```

will result in the following JSON to be merged in the Arduino IDE generated `launch.json`:

```json
{
"overrideRestartCommands": ["monitor reset halt", "monitor gdb_sync", "thb setup", "c"],
"postAttachCommands": [
"set remote hardware-watchpoint-limit 2",
"monitor reset halt",
"monitor gdb_sync",
"thb setup",
"c"
]
}
```

### Optimization level for debugging

The compiler optimization level that is appropriate for normal usage will often not provide a good experience while
debugging. For this reason, it may be helpful to use different compiler flags when compiling a sketch for use with the
debugger. The flags for use when compiling for debugging can be defined via the **compiler.optimization_flags.debug**
Expand Down
Loading

0 comments on commit 748fcf6

Please sign in to comment.