all: implement gdb sub-command for easy debugging
Этот коммит содержится в:
родитель
ef2ac09561
коммит
b08c8a0cf0
4 изменённых файлов: 122 добавлений и 3 удалений
39
colorwriter.go
Обычный файл
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
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"]
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче