Browse Source

main: show runtime panic addresses for `tinygo run`

This adds the same panic locations that are already present for
`tinygo flash -monitor`, but for `tinygo run` and `tinygo test`.

For example, this is the output that I get while working on some GC
code. It now shows the source location instead of just an address:

    $ tinygo test -v archive/zip
    === RUN   TestReader
    === RUN   TestReader/test.zip
    panic: runtime error at 0x000000000024d9b4: goroutine stack overflow
    [tinygo: panic at /home/ayke/src/tinygo/tinygo/src/internal/task/task_stack.go:58:15]
    FAIL    archive/zip     0.139s

(This particular location isn't all that useful, but it shows that the
feature works).
pull/4388/head
Ayke van Laethem 3 months ago
committed by Ayke
parent
commit
020664591a
  1. 2
      main.go
  2. 60
      monitor.go

2
main.go

@ -936,7 +936,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
// Configure stdout/stderr. The stdout may go to a buffer, not a real
// stdout.
cmd.Stdout = stdout
cmd.Stdout = newOutputWriter(stdout, result.Executable)
cmd.Stderr = os.Stderr
if config.EmulatorName() == "simavr" {
cmd.Stdout = nil // don't print initial load commands

60
monitor.go

@ -197,31 +197,14 @@ func Monitor(executable, port string, config *compileopts.Config) error {
go func() {
buf := make([]byte, 100*1024)
var line []byte
writer := newOutputWriter(os.Stdout, executable)
for {
n, err := serialConn.Read(buf)
if err != nil {
errCh <- fmt.Errorf("read error: %w", err)
return
}
start := 0
for i, c := range buf[:n] {
if c == '\n' {
os.Stdout.Write(buf[start : i+1])
start = i + 1
address := extractPanicAddress(line)
if address != 0 {
loc, err := addressToLine(executable, address)
if err == nil && loc.IsValid() {
fmt.Printf("[tinygo: panic at %s]\n", loc.String())
}
}
line = line[:0]
} else {
line = append(line, c)
}
}
os.Stdout.Write(buf[start:n])
writer.Write(buf[:n])
}
}()
@ -400,3 +383,42 @@ func readDWARF(executable string) (*dwarf.Data, error) {
return nil, errors.New("unknown binary format")
}
}
type outputWriter struct {
out io.Writer
executable string
line []byte
}
// newOutputWriter returns an io.Writer that will intercept panic addresses and
// will try to insert a source location in the output if the source location can
// be found in the executable.
func newOutputWriter(out io.Writer, executable string) *outputWriter {
return &outputWriter{
out: out,
executable: executable,
}
}
func (w *outputWriter) Write(p []byte) (n int, err error) {
start := 0
for i, c := range p {
if c == '\n' {
w.out.Write(p[start : i+1])
start = i + 1
address := extractPanicAddress(w.line)
if address != 0 {
loc, err := addressToLine(w.executable, address)
if err == nil && loc.Filename != "" {
fmt.Printf("[tinygo: panic at %s]\n", loc.String())
}
}
w.line = w.line[:0]
} else {
w.line = append(w.line, c)
}
}
w.out.Write(p[start:])
n = len(p)
return
}

Loading…
Cancel
Save