在Golang的FAQ 中,解释了为什么goroutine
是匿名的,没有暴露出ID等状态信息,主要是因为如果一个特定的goroutine
有了名字(ID),开发者就会忽略使用多个goroutine
来处理信息的可能,从而限制库的使用。
但是,有时候开发者也需要获取goroutine
来进行特定的操作。本文介绍如何从堆栈信息中获取goroutine信息 。
panic获取堆栈信息 当go程序panic
的时候,会打印出goroutine
的ID, 状态,函数,调用栈的信息。
1 2 3 func TestPanic (t *testing.T) { panic ("get stack info" ) }
堆栈信息
1 2 3 4 5 6 7 8 9 10 11 12 13 goroutine 18 [running]: testing.tRunner.func1.1(0x111b7e0, 0x116c710) /usr/local/go/src/testing/testing.go:1076 +0x30d testing.tRunner.func1(0xc000082600) /usr/local/go/src/testing/testing.go:1079 +0x41a panic(0x111b7e0, 0x116c710) /usr/local/go/src/runtime/panic.go:969 +0x175 awesomeProject/goid.TestPanic(0xc000082600) /Users/liangyaopei/go/src/awesomeProject/goid/goid_test.go:6 +0x39 testing.tRunner(0xc000082600, 0x114d490) /usr/local/go/src/testing/testing.go:1127 +0xef created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1178 +0x38
可以看到,goroutine
的ID是18,当前状态是running
,调用的函数是testing.tRunner.func1
。
goroutine的状态 在/runtime/traceback.go 中,可以看到goroutine
一共有以下的状态:
1 2 3 4 5 6 7 8 9 10 var gStatusStrings = [...]string { _Gidle: "idle" , _Grunnable: "runnable" , _Grunning: "running" , _Gsyscall: "syscall" , _Gwaiting: "waiting" , _Gdead: "dead" , _Gcopystack: "copystack" , _Gpreempted: "preempted" , }
runtime.Stack获取堆栈信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package runtimefunc Stack (buf []byte , all bool ) int { if all { stopTheWorld("stack trace" ) } ... if all { startTheWorld() } }
runtime.Stack()
,接受2个参数,一个是保存信息的字符串数字,另一个all
表示是否要打印全部的堆栈信息。如果all
为true,就会先调用stopTheWorld
看下面的测试:
1 2 3 4 5 6 7 8 9 func TestGetStackInfo (t *testing.T) { var ( size = 64 * 1024 all = false ) buf := make ([]byte , size) runtime.Stack(buf, all) t.Logf("%s" , string (buf)) }
输入结果:
1 2 3 4 5 6 7 goid_test.go:19: goroutine 18 [running]: awesomeProject/goid.TestGetStackInfo(0xc000082600) /Users/liangyaopei/go/src/awesomeProject/goid/goid_test.go:18 +0x6f testing.tRunner(0xc000082600, 0x114d5c0) /usr/local/go/src/testing/testing.go:1127 +0xef created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1178 +0x386
可以看到,输入的结果都是有格式的。
解析堆栈信息,获取goroutine ID 在stack.go 中,通过调用runtime.Stack
函数,获取当前的堆栈信息,然后解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 func GetInfo (all bool ) []Stack { var stacks []Stack var curStack *Stack stackReader := bufio.NewReader(bytes.NewReader(getStackBuffer(all))) for { line, err := stackReader.ReadString('\n' ) if err == io.EOF { break } if err != nil { panic ("bufio.NewReader failed on a fixed string" ) } isFirstLine := false if strings.HasPrefix(line, "goroutine " ) { if curStack != nil { stacks = append (stacks, *curStack) } id, goState := parseGoStackHeader(line) curStack = &Stack{ id: id, state: goState, fullStack: &bytes.Buffer{}, timeStamp: time.Now().UnixNano(), } isFirstLine = true } curStack.fullStack.WriteString(line) if !isFirstLine && curStack.firstFunction == "" { curStack.firstFunction = parseFirstFunc(line) } } if curStack != nil { stacks = append (stacks, *curStack) } return stacks }
通过解析以下的信息格式,获取对于的id。
1 xxx.go :19 : goroutine xx [xx] :
我的公众号:lyp_share
我的知乎专栏
我的博客com
我的博客cn