Browse Source

all: implement gdb sub-command for easy debugging

pull/36/head
Ayke van Laethem 6 years ago
parent
commit
b08c8a0cf0
No known key found for this signature in database GPG Key ID: E97FF5335DFDFDED
  1. 39
      colorwriter.go
  2. 75
      main.go
  3. 6
      target.go
  4. 5
      targets/pca10040.json

39
colorwriter.go

@ -0,0 +1,39 @@
package main
import (
"io"
)
// ANSI escape codes for terminal colors.
const (
TermColorReset = "\x1b[0m"
TermColorYellow = "\x1b[93m"
)
// ColorWriter wraps an io.Writer but adds a prefix and a terminal color.
type ColorWriter struct {
Out io.Writer
Color string
Prefix string
line []byte
}
// Write implements io.Writer, but with an added prefix and terminal color.
func (w *ColorWriter) Write(p []byte) (n int, err error) {
for _, c := range p {
if c == '\n' {
w.line = append(w.line, []byte(TermColorReset)...)
w.line = append(w.line, '\n')
// Write this line.
_, err := w.Out.Write(w.line)
w.line = w.line[:0]
w.line = append(w.line, []byte(w.Color+w.Prefix)...)
if err != nil {
return 0, err
}
} else {
w.line = append(w.line, c)
}
}
return len(p), nil
}

75
main.go

@ -8,8 +8,10 @@ import (
"io/ioutil"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
"github.com/aykevl/go-llvm"
"github.com/aykevl/tinygo/compiler"
@ -199,6 +201,70 @@ func Flash(pkgName, target, port string, printIR, dumpSSA, debug bool, printSize
})
}
// Flash a program on a microcontroller and drop into a GDB shell.
//
// Note: this command is expected to execute just before exiting, as it
// modifies global state.
func FlashGDB(pkgName, target, port string, printIR, dumpSSA bool, printSizes string) error {
spec, err := LoadTarget(target)
if err != nil {
return err
}
if spec.GDB == "" {
return errors.New("gdb not configured in the target specification")
}
debug := true // always enable debug symbols
return Compile(pkgName, "", spec, printIR, dumpSSA, debug, printSizes, func(tmppath string) error {
if len(spec.OCDDaemon) != 0 {
// We need a separate debugging daemon for on-chip debugging.
daemon := exec.Command(spec.OCDDaemon[0], spec.OCDDaemon[1:]...)
// Make it clear which output is from the daemon.
daemon.Stderr = &ColorWriter{
Out: os.Stderr,
Prefix: spec.OCDDaemon[0] + ": ",
Color: TermColorYellow,
}
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// GDB (to break the currently executing program).
// https://stackoverflow.com/a/35435038/559350
daemon.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Pgid: 0,
}
// Start now, and kill it on exit.
daemon.Start()
defer func() {
daemon.Process.Signal(os.Interrupt)
// Maybe we should send a .Kill() after x seconds?
daemon.Wait()
}()
}
// Ignore Ctrl-C, it must be passed on to GDB.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
}
}()
// Construct and execute a gdb command.
// By default: gdb -ex run <binary>
// Exit GDB with Ctrl-D.
params := []string{tmppath}
for _, cmd := range spec.GDBCmds {
params = append(params, "-ex", cmd)
}
cmd := exec.Command(spec.GDB, params...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
})
}
// Run the specified package directly (using JIT or interpretation).
func Run(pkgName string) error {
config := compiler.Config{
@ -284,13 +350,18 @@ func main() {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
case "flash":
case "flash", "gdb":
if *outpath != "" {
fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.")
usage()
os.Exit(1)
}
err := Flash(flag.Arg(0), *target, *port, *printIR, *dumpSSA, !*nodebug, *printSize)
var err error
if command == "flash" {
err = Flash(flag.Arg(0), *target, *port, *printIR, *dumpSSA, !*nodebug, *printSize)
} else {
err = FlashGDB(flag.Arg(0), *target, *port, *printIR, *dumpSSA, *printSize)
}
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)

6
target.go

@ -20,6 +20,9 @@ type TargetSpec struct {
PreLinkArgs []string `json:"pre-link-args"`
Objcopy string `json:"objcopy"`
Flasher string `json:"flash"`
OCDDaemon []string `json:"ocd-daemon"`
GDB string `json:"gdb"`
GDBCmds []string `json:"gdb-initial-cmds"`
}
// Load a target specification
@ -30,6 +33,8 @@ func LoadTarget(target string) (*TargetSpec, error) {
Linker: "cc",
PreLinkArgs: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie
Objcopy: "objcopy",
GDB: "gdb",
GDBCmds: []string{"run"},
}
// See whether there is a target specification for this target (e.g.
@ -37,6 +42,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
path := filepath.Join(sourceDir(), "targets", strings.ToLower(target)+".json")
if fp, err := os.Open(path); err == nil {
defer fp.Close()
*spec = TargetSpec{} // reset all fields
err := json.NewDecoder(fp).Decode(spec)
if err != nil {
return nil, err

5
targets/pca10040.json

@ -4,5 +4,8 @@
"linker": "arm-none-eabi-gcc",
"pre-link-args": ["-nostdlib", "-nostartfiles", "-mcpu=cortex-m4", "-mthumb", "-T", "targets/nrf52.ld", "-Wl,--gc-sections", "-fno-exceptions", "-fno-unwind-tables", "-ffunction-sections", "-fdata-sections", "-Os", "-DNRF52832_XXAA", "-Ilib/CMSIS/CMSIS/Include", "lib/nrfx/mdk/system_nrf52.c", "src/device/nrf/nrf52.s"],
"objcopy": "arm-none-eabi-objcopy",
"flash": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset"
"flash": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset",
"ocd-daemon": ["openocd", "-f", "interface/jlink.cfg", "-c", "transport select swd", "-f", "target/nrf51.cfg"],
"gdb": "arm-none-eabi-gdb",
"gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"]
}

Loading…
Cancel
Save