登录
首页 >  Golang >  Go问答

生成Go调用图

来源:stackoverflow

时间:2024-03-01 11:00:24 261浏览 收藏

积累知识,胜过积蓄金银!毕竟在Golang开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《生成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

今天带大家了解了的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>