mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
The previous commit started printing the instruction address for runtime panics. This commit starts using this address to print the source location. Here is an example where this feature is very useful. There is a heap allocation in the Bluetooth package, but we don't know where exactly. Printing the instruction address of the panic is already useful, but what is even more useful is looking up this address in the DWARF debug information that's part of the binary: $ tinygo flash -target=circuitplay-bluefruit -monitor ./examples/heartrate Connected to /dev/ttyACM0. Press Ctrl-C to exit. tick 00:00.810 tick 00:01.587 tick 00:02.387 tick 00:03.244 panic: runtime error at 0x00027c4d: alloc in interrupt [tinygo: panic at /home/ayke/src/tinygo/bluetooth/adapter_sd.go:74:4] To be clear, this path isn't stored on the microcontroller. It's stored as part of the build, and `-monitor` just looks up the path from the panic message. Possible enhancements: - Print such an address for regular panics as well. I'm not sure that's so useful, as it's usually a lot easier to look up panics just by their message. - Use runtimePanicAt (instead of runtimePanic) in other locations, if that proves to be beneficial. - Print the TinyGo-generated output in some other color, to distinguish it from the regular console output. - Print more details when panicking (registers, stack values), and print an actual backtrace.pull/3527/merge
Ayke van Laethem
2 years ago
committed by
Ron Evans
4 changed files with 209 additions and 7 deletions
@ -0,0 +1,66 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bytes" |
|||
"os/exec" |
|||
"path/filepath" |
|||
"runtime" |
|||
"testing" |
|||
"time" |
|||
|
|||
"github.com/tinygo-org/tinygo/builder" |
|||
"github.com/tinygo-org/tinygo/compileopts" |
|||
) |
|||
|
|||
func TestTraceback(t *testing.T) { |
|||
if runtime.GOOS != "linux" { |
|||
// We care about testing the ELF format, which is only used on Linux
|
|||
// (not on MacOS or Windows).
|
|||
t.Skip("Test only works on Linux") |
|||
} |
|||
|
|||
// Build a small binary that only panics.
|
|||
tmpdir := t.TempDir() |
|||
config, err := builder.NewConfig(&compileopts.Options{ |
|||
GOOS: runtime.GOOS, |
|||
GOARCH: runtime.GOARCH, |
|||
Opt: "z", |
|||
InterpTimeout: time.Minute, |
|||
Debug: true, |
|||
}) |
|||
if err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
result, err := builder.Build("testdata/trivialpanic.go", ".elf", tmpdir, config) |
|||
if err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
|
|||
// Run this binary, and capture the output.
|
|||
buf := &bytes.Buffer{} |
|||
cmd := exec.Command(result.Binary) |
|||
cmd.Stdout = buf |
|||
cmd.Stderr = buf |
|||
cmd.Run() // this will return an error because of the panic, ignore it
|
|||
|
|||
// Extract the "runtime error at" address.
|
|||
line := bytes.TrimSpace(buf.Bytes()) |
|||
address := extractPanicAddress(line) |
|||
if address == 0 { |
|||
t.Fatalf("could not extract panic address from %#v", string(line)) |
|||
} |
|||
|
|||
// Look up the source location for this address.
|
|||
location, err := addressToLine(result.Executable, address) |
|||
if err != nil { |
|||
t.Fatal("could not read source location:", err) |
|||
} |
|||
|
|||
// Verify that the source location is as expected.
|
|||
if filepath.Base(location.Filename) != "trivialpanic.go" { |
|||
t.Errorf("expected path to end with trivialpanic.go, got %#v", location.Filename) |
|||
} |
|||
if location.Line != 6 { |
|||
t.Errorf("expected panic location to be line 6, got line %d", location.Line) |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
package main |
|||
|
|||
var n *int |
|||
|
|||
func main() { |
|||
println(*n) // this will panic
|
|||
} |
Loading…
Reference in new issue