打印 Go 调用树
Print Go call tree
给出这样的文件:
package main
func A() {}
func B() {
A()
}
func C() {
A()
}
func D() {
B()
}
func E() {
B()
}
func F() {
C()
}
func G() {
C()
}
func main() {
D()
E()
F()
G()
}
我想打印程序的调用树,像这样:
main
D
B
A
E
B
A
F
C
A
G
C
A
我找到了 callgraph
程序 [1],但它没有创建树:
PS C:\prog> callgraph .
prog.A --static-4:5--> prog.C
prog.A --static-5:5--> prog.D
prog.main --static-19:5--> prog.A
prog.B --static-9:5--> prog.E
prog.B --static-10:5--> prog.F
prog.main --static-20:5--> prog.B
有什么方法可以做到这一点吗?
所以我确实找到了一个似乎可以处理从图表上打印树的包
命令行 [1]。然而我又想了想,打印出一棵树
可能不是解决我的问题的最佳方法。我想做的是 return 一个
我的一个功能出错。但是要做到这一点,我需要传播
错误一直到 main
。因为这可以是几层,我认为
如果我从 main
开始,然后逐步达到所需的效果,那将是最好的
功能。这样,如果需要,我可以分阶段进行工作。问题是,我该如何
获得这些功能的有序列表?我找到了 tsort
[2]:
的解决方案
PS C:\> callgraph -format digraph . | coreutils tsort
"init/test.main"
"init/test.D"
"init/test.E"
"init/test.F"
"init/test.G"
"init/test.B"
"init/test.C"
"init/test.A"
但我可能并不总是想要整个调用图。接下来我想只添加
panic
:
func A() {
panic(1)
}
但这不会给你所有的分支,只有第一条通往目标的路径
功能:
main.A(...)
C:/test.go:4
main.B(...)
C:/test.go:8
main.D(...)
C:/test.go:16
main.main()
C:/test.go:32 +0x45
最后我写了我自己的排序函数,它将任意目的地作为输入,
并按从 main
到目标函数的顺序打印所有路径:
package main
func tsort(graph map[string][]string, end string) []string {
var (
b = make(map[string]bool)
l []string
s = []string{end}
)
for len(s) > 0 {
n := s[len(s) - 1]
b[n] = true
for _, m := range graph[n] {
if ! b[m] {
s = append(s, m)
}
}
if s[len(s) - 1] == n {
s = s[:len(s) - 1]
l = append(l, n)
}
}
return l
}
示例:
package main
import (
"bytes"
"fmt"
"os/exec"
)
func main() {
b := new(bytes.Buffer)
c := exec.Command("callgraph", "-format", "digraph", ".")
c.Stdout = b
c.Run()
m := make(map[string][]string)
for {
var parent, child string
_, e := fmt.Fscanln(b, &parent, &child)
if e != nil { break }
m[child] = append(m[child], parent)
}
for n, s := range tsort(m, `"init/test.A"`) {
fmt.Print(n+1, ". ", s, "\n")
}
}
结果:
1. "init/test.main"
2. "init/test.G"
3. "init/test.F"
4. "init/test.C"
5. "init/test.D"
6. "init/test.E"
7. "init/test.B"
8. "init/test.A"
给出这样的文件:
package main
func A() {}
func B() {
A()
}
func C() {
A()
}
func D() {
B()
}
func E() {
B()
}
func F() {
C()
}
func G() {
C()
}
func main() {
D()
E()
F()
G()
}
我想打印程序的调用树,像这样:
main
D
B
A
E
B
A
F
C
A
G
C
A
我找到了 callgraph
程序 [1],但它没有创建树:
PS C:\prog> callgraph .
prog.A --static-4:5--> prog.C
prog.A --static-5:5--> prog.D
prog.main --static-19:5--> prog.A
prog.B --static-9:5--> prog.E
prog.B --static-10:5--> prog.F
prog.main --static-20:5--> prog.B
有什么方法可以做到这一点吗?
所以我确实找到了一个似乎可以处理从图表上打印树的包
命令行 [1]。然而我又想了想,打印出一棵树
可能不是解决我的问题的最佳方法。我想做的是 return 一个
我的一个功能出错。但是要做到这一点,我需要传播
错误一直到 main
。因为这可以是几层,我认为
如果我从 main
开始,然后逐步达到所需的效果,那将是最好的
功能。这样,如果需要,我可以分阶段进行工作。问题是,我该如何
获得这些功能的有序列表?我找到了 tsort
[2]:
PS C:\> callgraph -format digraph . | coreutils tsort
"init/test.main"
"init/test.D"
"init/test.E"
"init/test.F"
"init/test.G"
"init/test.B"
"init/test.C"
"init/test.A"
但我可能并不总是想要整个调用图。接下来我想只添加
panic
:
func A() {
panic(1)
}
但这不会给你所有的分支,只有第一条通往目标的路径 功能:
main.A(...)
C:/test.go:4
main.B(...)
C:/test.go:8
main.D(...)
C:/test.go:16
main.main()
C:/test.go:32 +0x45
最后我写了我自己的排序函数,它将任意目的地作为输入,
并按从 main
到目标函数的顺序打印所有路径:
package main
func tsort(graph map[string][]string, end string) []string {
var (
b = make(map[string]bool)
l []string
s = []string{end}
)
for len(s) > 0 {
n := s[len(s) - 1]
b[n] = true
for _, m := range graph[n] {
if ! b[m] {
s = append(s, m)
}
}
if s[len(s) - 1] == n {
s = s[:len(s) - 1]
l = append(l, n)
}
}
return l
}
示例:
package main
import (
"bytes"
"fmt"
"os/exec"
)
func main() {
b := new(bytes.Buffer)
c := exec.Command("callgraph", "-format", "digraph", ".")
c.Stdout = b
c.Run()
m := make(map[string][]string)
for {
var parent, child string
_, e := fmt.Fscanln(b, &parent, &child)
if e != nil { break }
m[child] = append(m[child], parent)
}
for n, s := range tsort(m, `"init/test.A"`) {
fmt.Print(n+1, ". ", s, "\n")
}
}
结果:
1. "init/test.main"
2. "init/test.G"
3. "init/test.F"
4. "init/test.C"
5. "init/test.D"
6. "init/test.E"
7. "init/test.B"
8. "init/test.A"