all: implement gdb sub-command for easy debugging

Этот коммит содержится в:
Ayke van Laethem 2018-10-03 19:01:25 +02:00
родитель ef2ac09561
коммит b08c8a0cf0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
4 изменённых файлов: 122 добавлений и 3 удалений

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" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"github.com/aykevl/go-llvm" "github.com/aykevl/go-llvm"
"github.com/aykevl/tinygo/compiler" "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). // Run the specified package directly (using JIT or interpretation).
func Run(pkgName string) error { func Run(pkgName string) error {
config := compiler.Config{ config := compiler.Config{
@ -284,13 +350,18 @@ func main() {
fmt.Fprintln(os.Stderr, "error:", err) fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1) os.Exit(1)
} }
case "flash": case "flash", "gdb":
if *outpath != "" { if *outpath != "" {
fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.") fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.")
usage() usage()
os.Exit(1) 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 { if err != nil {
fmt.Fprintln(os.Stderr, "error:", err) fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1) os.Exit(1)

Просмотреть файл

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

Просмотреть файл

@ -4,5 +4,8 @@
"linker": "arm-none-eabi-gcc", "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"], "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", "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"]
} }