aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2024-08-05 13:34:02 +0200
committerAyke <[email protected]>2024-08-06 13:28:07 +0200
commit020664591ab3a995d6d0aab5097c6fab838a925c (patch)
tree20b3600a388e96d36d6d05ea16b4a90510e73a4e
parent2d6d9eb76df322a7970dff5183f15ff8dc545301 (diff)
downloadtinygo-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.go2
-rw-r--r--monitor.go60
2 files changed, 42 insertions, 20 deletions
diff --git a/main.go b/main.go
index 3518d4b54..c90bf7e69 100644
--- a/main.go
+++ b/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
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
+}