Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions builder/sizes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test.
tests := []sizeTest{
// microcontrollers
{"hifive1b", "examples/echo", 4277, 307, 0, 2260},
{"microbit", "examples/serial", 2836, 368, 8, 2256},
{"wioterminal", "examples/pininterrupt", 8013, 1663, 132, 7488},
{"hifive1b", "examples/echo", 4313, 323, 0, 2260},
{"microbit", "examples/serial", 2838, 382, 8, 2256},
{"wioterminal", "examples/pininterrupt", 8027, 1665, 132, 7488},

// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
Expand Down
44 changes: 36 additions & 8 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -988,15 +988,15 @@ func TestGoexitCrash(t *testing.T) {
name string
want string
}{
{"main", "all goroutines are asleep - deadlock!"},
{"deadlock", "all goroutines are asleep - deadlock!"},
{"exit", "all goroutines are asleep - deadlock!"},
{"main-other", "all goroutines are asleep - deadlock!"},
{"in-panic", "all goroutines are asleep - deadlock!"},
{"main", "fatal error: all goroutines are asleep - deadlock!"},
{"deadlock", "fatal error: all goroutines are asleep - deadlock!"},
{"exit", "fatal error: all goroutines are asleep - deadlock!"},
{"main-other", "fatal error: all goroutines are asleep - deadlock!"},
{"in-panic", "fatal error: all goroutines are asleep - deadlock!"},
{"panic", "panic: panic after Goexit"},
{"recovered-panic", "all goroutines are asleep - deadlock!"},
{"recover-before-panic", "all goroutines are asleep - deadlock!"},
{"recover-before-panic-loop", "all goroutines are asleep - deadlock!"},
{"recovered-panic", "fatal error: all goroutines are asleep - deadlock!"},
{"recover-before-panic", "fatal error: all goroutines are asleep - deadlock!"},
{"recover-before-panic-loop", "fatal error: all goroutines are asleep - deadlock!"},
} {
t.Run(tc.name, func(t *testing.T) {
output := &bytes.Buffer{}
Expand All @@ -1017,6 +1017,34 @@ func TestGoexitCrash(t *testing.T) {
}
}

func TestRuntimeFatal(t *testing.T) {
t.Parallel()

options := optionsFromTarget("", sema)
config, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}

output := &bytes.Buffer{}
_, err = buildAndRun("testdata/runtimefatal.go", config, output, nil, nil, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
cmd.Stdout = nil
cmd.Stderr = nil
data, err := cmd.CombinedOutput()
output.Write(data)
return err
})
if err == nil {
t.Fatal("program unexpectedly exited successfully")
}
if want := "fatal error: sync: unlock of unlocked Mutex"; !strings.Contains(output.String(), want) {
t.Fatalf("output does not contain %q:\n%s", want, output.String())
}
if strings.Contains(output.String(), "recovered:") {
t.Fatalf("fatal runtime error was recovered:\n%s", output.String())
}
}

func TestTest(t *testing.T) {
t.Parallel()

Expand Down
6 changes: 6 additions & 0 deletions src/internal/task/fatal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package task

import _ "unsafe"

//go:linkname runtimeFatal runtime.runtimeFatal
func runtimeFatal(msg string)
2 changes: 1 addition & 1 deletion src/internal/task/mutex-cooperative.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (m *Mutex) Lock() {

func (m *Mutex) Unlock() {
if !m.locked {
panic("sync: unlock of unlocked Mutex")
runtimeFatal("sync: unlock of unlocked Mutex")
}

// Wake up a blocked task, if applicable.
Expand Down
2 changes: 1 addition & 1 deletion src/internal/task/mutex-preemptive.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (m *Mutex) Lock() {
func (m *Mutex) Unlock() {
if old := m.futex.Swap(0); old == 0 {
// Mutex wasn't locked before.
panic("sync: unlock of unlocked Mutex")
runtimeFatal("sync: unlock of unlocked Mutex")
} else if old == 2 {
// Mutex was a contended lock, so we need to wake the next waiter.
m.futex.Wake()
Expand Down
4 changes: 2 additions & 2 deletions src/internal/task/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (q *Queue) Push(t *Task) {
mask := lockAtomics()
if asserts && t.Next != nil {
unlockAtomics(mask)
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
runtimeFatal("runtime: pushing a task to a queue with a non-nil Next pointer")
}
if q.tail != nil {
q.tail.Next = t
Expand Down Expand Up @@ -78,7 +78,7 @@ func (s *Stack) Push(t *Task) {
mask := lockAtomics()
if asserts && t.Next != nil {
unlockAtomics(mask)
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
runtimeFatal("runtime: pushing a task to a stack with a non-nil Next pointer")
}
s.top, t.Next = t, s.top
unlockAtomics(mask)
Expand Down
7 changes: 2 additions & 5 deletions src/internal/task/task_asyncify.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
// otherwise Go wouldn't allow the cast to a smaller integer size.
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))

//go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(str string)

// state is a structure which holds a reference to the state of the task.
// When the task is suspended, the stack pointers are saved here.
type state struct {
Expand Down Expand Up @@ -95,7 +92,7 @@ func Current() *Task {
// This function may only be called when running on a goroutine stack, not when running on the system stack.
func Pause() {
if *currentTask.state.canaryPtr != stackCanary {
runtimePanic("stack overflow")
runtimeFatal("stack overflow")
}

currentTask.state.unwind()
Expand Down Expand Up @@ -124,7 +121,7 @@ func (t *Task) Resume() {
currentTask = prevTask
t.gcData.swap()
if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) {
runtimePanic("stack overflow")
runtimeFatal("stack overflow")
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/internal/task/task_exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ func exit(goexit bool) {
if t == mainTask {
if goexit {
if remaining == 0 {
runtimePanic("all goroutines are asleep - deadlock!")
runtimeFatal("all goroutines are asleep - deadlock!")
}
atomic.StoreUint32(&mainExitedByGoexit, 1)
}
} else if atomic.LoadUint32(&mainExitedByGoexit) != 0 && remaining == 0 {
runtimePanic("all goroutines are asleep - deadlock!")
runtimeFatal("all goroutines are asleep - deadlock!")
}

// TODO: explicitly free the stack after switching back to the scheduler.
Pause()
runtimePanic("unreachable")
runtimeFatal("unreachable")
}
3 changes: 0 additions & 3 deletions src/internal/task/task_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import (
"unsafe"
)

//go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(str string)

// Stack canary, to detect a stack overflow. The number is a random number
// generated by random.org. The bit fiddling dance is necessary because
// otherwise Go wouldn't allow the cast to a smaller integer size.
Expand Down
4 changes: 2 additions & 2 deletions src/internal/task/task_stack_multicore.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ func PauseLocked() {
// valid. If it is not, a stack overflow has occurred.
current := Current()
if *current.state.canaryPtr != stackCanary {
runtimePanic("goroutine stack overflow")
runtimeFatal("goroutine stack overflow")
}
if interrupt.In() {
runtimePanic("blocked inside interrupt")
runtimeFatal("blocked inside interrupt")
}
if current.RunState == RunStateResuming {
// Another core already marked this goroutine as ready to resume.
Expand Down
4 changes: 2 additions & 2 deletions src/internal/task/task_stack_unicore.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ func Pause() {
// Check whether the canary (the lowest address of the stack) is still
// valid. If it is not, a stack overflow has occurred.
if *currentTask.state.canaryPtr != stackCanary {
runtimePanic("goroutine stack overflow")
runtimeFatal("goroutine stack overflow")
}
if interrupt.In() {
runtimePanic("blocked inside interrupt")
runtimeFatal("blocked inside interrupt")
}
currentTask.state.pause()
}
Expand Down
17 changes: 7 additions & 10 deletions src/internal/task/task_threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var activeTaskLock PMutex
var mainExitedByGoexit bool

func OnSystemStack() bool {
runtimePanic("todo: task.OnSystemStack")
runtimeFatal("todo: task.OnSystemStack")
return false
}

Expand All @@ -63,7 +63,7 @@ func Init(sp uintptr) {
func Current() *Task {
t := (*Task)(tinygo_task_current())
if t == nil {
runtimePanic("unknown current task")
runtimeFatal("unknown current task")
}
return t
}
Expand Down Expand Up @@ -112,7 +112,7 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
activeTaskLock.Lock()
errCode := tinygo_task_start(fn, args, t, &t.state.thread, &t.state.stackTop, stackSize)
if errCode != 0 {
runtimePanic("could not start thread")
runtimeFatal("could not start thread")
}
t.state.QueueNext = activeTasks
activeTasks = t
Expand All @@ -127,7 +127,7 @@ func taskExited(t *Task) {
}

if exit(t) {
runtimePanic("all goroutines are asleep - deadlock!")
runtimeFatal("all goroutines are asleep - deadlock!")
}
}

Expand All @@ -149,7 +149,7 @@ func exit(t *Task) bool {

// Sanity check.
if !found {
runtimePanic("taskExited failed")
runtimeFatal("taskExited failed")
}
return deadlocked
}
Expand All @@ -173,11 +173,11 @@ func Exit() {
}
activeTaskLock.Unlock()
if noOtherTasks {
runtimePanic("all goroutines are asleep - deadlock!")
runtimeFatal("all goroutines are asleep - deadlock!")
}
}
if exit(t) {
runtimePanic("all goroutines are asleep - deadlock!")
runtimeFatal("all goroutines are asleep - deadlock!")
}
tinygo_task_exit()
}
Expand Down Expand Up @@ -325,9 +325,6 @@ func StackTop() uintptr {
return Current().state.stackTop
}

//go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(msg string)

// Using //go:linkname instead of //export so that we don't tell the compiler
// that the 't' parameter won't escape (because it will).
//
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/arch_tinygowasm_malloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func libc_free(ptr unsafe.Pointer) {
if _, ok := allocs[(*byte)(ptr)]; ok {
delete(allocs, (*byte)(ptr))
} else {
panic("free: invalid pointer")
runtimeFatal("free: invalid pointer")
}
}

Expand Down Expand Up @@ -63,7 +63,7 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer {
copy(newBuf, oldBuf)
delete(allocs, (*byte)(oldPtr))
} else {
panic("realloc: invalid pointer")
runtimeFatal("realloc: invalid pointer")
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/dynamic_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func dynamicLoader(base uintptr, dyn *dyn64) {
}

if rela == nil {
runtimePanic("bad reloc")
runtimeFatal("bad reloc")
}

if debugLoader {
Expand Down
Loading
Loading