diff options
author | Ayke van Laethem <[email protected]> | 2024-08-05 13:34:02 +0200 |
---|---|---|
committer | Ayke <[email protected]> | 2024-08-06 13:28:07 +0200 |
commit | 020664591ab3a995d6d0aab5097c6fab838a925c (patch) | |
tree | 20b3600a388e96d36d6d05ea16b4a90510e73a4e | |
parent | 2d6d9eb76df322a7970dff5183f15ff8dc545301 (diff) | |
download | tinygo-020664591ab3a995d6d0aab5097c6fab838a925c.tar.gz tinygo-020664591ab3a995d6d0aab5097c6fab838a925c.zip |
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).
-rw-r--r-- | main.go | 2 | ||||
-rw-r--r-- | monitor.go | 60 |
2 files changed, 42 insertions, 20 deletions
@@ -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 diff --git a/monitor.go b/monitor.go index 7b9c89643..46f3de927 100644 --- a/monitor.go +++ b/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 +} |