Skip to content

Commit

Permalink
main: show runtime panic addresses for tinygo run
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
aykevl committed Aug 6, 2024
1 parent 2d6d9eb commit 0206645
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 20 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 41 additions & 19 deletions monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}()

Expand Down Expand Up @@ -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
}

0 comments on commit 0206645

Please sign in to comment.