Skip to content

Latest commit

 

History

History
232 lines (207 loc) · 12.7 KB

go.org

File metadata and controls

232 lines (207 loc) · 12.7 KB

Program

package main

import "fmt"

func main() {
	fmt.Printf("Hello, world.\n")
}

Startup

# Find the entry point
ENTRY_TEST=$(readelf -h test | grep Entry | awk '{print $4}')

# Start gdb and set the breakpoint at entry
gdb -ex "break *${TEST_ENTRY}"  ./test

FAQs

How goroutines are “interrupted/stopped”?

Since Go is using cooperative preemption, the goroutines have preemption points that will do the self stop/preemption. This may trip us under not-so-common circumstances if our code doesn’t have those preemption points. For example, the following program will not stop (tested in go1.7.3):

package main

import (
	"fmt"
	"runtime"
	"time"
)

func testfunc1() {
	num := 0
	for i := 1; i < 10000; i++ {
		num = num + i
	}
}

func busyFunc() {
	for {
		testfunc1()
	}
}

func main() {
	runtime.GOMAXPROCS(1)

	go busyFunc()

	time.Sleep(1 * time.Second)

	fmt.Println(" Hello world")
}

As we saw in the above startup sequence, even though the sysmon can detect that goroutine has been busy for long, it will only indicate the intent to preempt by setting a flag in the respective goroutine, which the respective goroutine checks at (compile time inserted) premption points (like function prologue).

The compiler doesn’t generate function prologue if it is a leaf function or if the stack is small.

To “verify” that, for example, if you use the below modified testfunc1() function, the premeption check will be generated (since it is no longer a small stack)

func testfunc1() {
	var a [128]byte
	a[0] = 0

	num := 0
	for i := 1; i < 10000; i++ {
		num = num + i
	}
}

Or add a division, which will insert a runtime.panicdivide call (to detect divide by zero), which will make this non-leaf function.

func testfunc1() {
	num := 0
	for i := 1; i < 10000; i++ {
		num = num + i/i
	}
}

Though note that as of go1.8, runtime.panicdivide will not generate a function prologue