打印 Go 调用树

问题描述

给定一个这样的文件:

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. https://github.com/golang/tools/blob/master/cmd/callgraph

解决方法

所以我确实找到了一个包,它似乎可以处理从图形上的图形打印树 命令行 [1]。然而我又想了想,还有一棵印刷的树 可能不是我问题的最佳解决方案。我想做的是返回一个 我的功能之一出错。但是要做到这一点,我需要传播 错误一直到 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"
  1. https://github.com/soniakeys/graph/blob/master/treevis/treevis.go
  2. https://github.com/uutils/coreutils

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...