Golang、pprof

我们可以使用go tool pprof命令来交互式的访问概要文件的内容。命令将会分析指定的概要文件,并会根据我们的要求为我们提供高可读性的输出信息。
我们可以通过标准库的代码包runtime和runtime/pprof中的程序来生成三种包含实时性数据的概要文件,分别是CPU概要文件、内存概要文件和程序阻塞概要文件

1. pprof工具在交互模式下支持的命令

| 名称 | 参数 | 标签 | 说明 |

1.1 | gv | [focus] | 将当前概要文件以图形化和层次化的形式显示出来。当没有任何参数时,在概要文件中的所有抽样都会被显示。如果指定了focus参数,则只显示调用栈中有名称与此参数相匹配的函数或方法的抽样。focus参数应该是一个正则表达式。

1.2 | web | [focus] | 与gv命令类似,web命令也会用图形化的方式来显示概要文件。但不同的是,web命令是在一个Web浏览器中显示它。如果你的Web浏览器已经启动,那么它的显示速度会非常快。如果想改变所使用的Web浏览器,可以在Linux下设置符号链接/etc/alternatives/gnome-www-browser或/etc/alternatives/x-www-browser,或在OS X下改变SVG文件的关联Finder。

1.3 | list | [routineregexp] | 列出名称与参数“routineregexp”代表的正则表达式相匹配的函数或方法的相关源代码。

1.4 | weblist | [routine_regexp] | 在Web浏览器中显示与list命令的输出相同的内容。它与list命令相比的优势是,在我们点击某行源码时还可以显示相应的汇编代码。

1.5 | top[N] | [–cum] | top命令可以以本地取样计数为顺序列出函数或方法及相关信息。如果存在标记“–cum”则以累积取样计数为顺序。默认情况下top命令会列出前10项内容。但是如果在top命令后面紧跟一个数字,那么其列出的项数就会与这个数字相同。

1.6 | disasm | [routineregexp] | 显示名称与参数“routineregexp”相匹配的函数或方法的反汇编代码。并且,在显示的内容中还会标注有相应的取样计数。

1.7 | callgrind | [filename] | 利用callgrind工具生成统计文件。在这个文件中,说明了程序中函数的调用情况。如果未指定“filename”参数,则直接调用kcachegrind工具。kcachegrind可以以可视化的方式查看callgrind工具生成的统计文件。

1.8 | help | 显示帮助信息。

1.9 | quit | 退出go tool pprof命令。Ctrl-d也可以达到同样效果。

2 CPU的概要文件:我们可以通过以下代码启动对CPU使用情况的记录。

func main() {
    f, err := os.OpenFile("./cpu.prof", os.O_RDWR|os.O_CREATE, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    for i := 0; i < 1000000; i++ {
        fmt.Println(i)
    }

    //注意,有时候defer f.Close(),defer pprof.StopCPUProfile() 会执行不到,这时候我们就会看到 prof文件是空的,我们需要在自己代码退出的地方,增加上下面两行,确保写文件内容了。

    pprof.StopCPUProfile()
    f.Close()
}

对产生的文件进行分析:

E:\GoWorks>go tool pprof  ./cpu.prof
Entering interactive mode (type "help" for commands)
(pprof) top
45.40s of 45.40s total (  100%)
      flat  flat%   sum%        cum   cum%
    45.40s   100%   100%     45.40s   100%
(pprof)  

3 内存概要的文件

  1. 内存概要文件用于保存在用户程序执行期间的内存使用情况。这里所说的内存使用情况,其实就是程序运行过程中堆内存的分配情况。Go语言运行时系统会对用户程序运行期间的所有的堆内存分配进行记录。不论在取样的那一时刻、堆内存已用字节数是否有增长,只要有字节被分配且数量足够,分析器就会对其进行取样。开启内存使用情况记录的方式如下:

    func startMemProfile() {
        if *memProfile != "" && *memProfileRate > 0 {
            runtime.MemProfileRate = *memProfileRate
        }
    }  
    

    在函数startMemProfile中,只有在memProfile和memProfileRate的值有效时才会进行后续操作。memProfile的含义是内存概要文件的绝对路径。memProfileRate的含义是分析器的取样间隔,单位是字节。当我们将这个值赋给int类型的变量runtime.MemProfileRate时,就意味着分析器将会在每分配指定的字节数量后对内存使用情况进行取样。实际上,即使我们不给runtime.MemProfileRate变量赋值,内存使用情况的取样操作也会照样进行。此取样操作会从用户程序开始时启动,且一直持续进行到用户程序结束。runtime.MemProfileRate变量的默认值是512 * 1024,即512K个字节。只有当我们显式的将0赋给runtime.MemProfileRate变量之后,才会取消取样操作。

  2. 内存使用情况的取样数据只会被保存在运行时内存中,而保存到文件的操作只能由我们自己来完成。请看如下代码:

    func stopMemProfile() {
        if *memProfile != "" {
            f, err := os.Create(*memProfile)
            if err != nil {
                fmt.Fprintf(os.Stderr, "Can not create mem profile output file: %s", err)
                return
            }
            if err = pprof.WriteHeapProfile(f); err != nil {
                fmt.Fprintf(os.Stderr, "Can not write %s: %s", *memProfile, err)
            }
            f.Close()
        }
    }  
    

    stopMemProfile函数的功能是停止对内存使用情况的取样操作。但是,它只做了将取样数据保存到内存概要文件的操作。在stopMemProfile函数中,我们调用了函数pprof.WriteHeapProfile,并把代表内存概要文件的文件实例作为了参数。如果pprof.WriteHeapProfile函数没有返回错误,就说明数据已被写入到了内存概要文件中

4 其他四种可从pprof.Lookup函数中取出的记录:goroutine、threadcreate、heap、block:

func SaveProfile(workDir string, profileName string, ptype string, debug int) {

    execFileAbsPath, _ := filepath.Abs(workDir)
    if profileName == "" {
        profileName = string(ptype)
    }
    profilePath := filepath.Join(execFileAbsPath, profileName)
    f, err := os.Create(profilePath)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Can not create profile output file: %s", err)
        return
    }
    if err = pprof.Lookup(string(ptype)).WriteTo(f, debug); err != nil {
        fmt.Fprintf(os.Stderr, "Can not write %s: %s", profilePath, err)
    }
    f.Close()
}
func main() {
    //SaveProfile("E://GoWorks", "goroutine.prof", "goroutine", 1)
    for i := 0; i < 10000; i++ {
        fmt.Println(i)
    }
}   

函数SaveProfile有四个参数。第一个参数是概要文件的存放目录。第二个参数是概要文件的名称。第三个参数是概要文件的类型。其中,类型ProfileType只是为string类型起的一个别名而已。这样是为了对它的值进行限制。它的值必须为“goroutine”、“threadcreate”、“heap”或“block”中的一个。我们现在来重点说一下第四个参数。参数debug控制着概要文件中信息的详细程度。这个参数也就是传给结构体pprof.Profile的指针方法WriteTo的第二个参数。而pprof.Profile结构体的实例的指针由函数pprof.Lookup产生。

5 Web测试

5.1 生成web服务器性能监控图

如果你的go程序是用http包启动的web服务器,你想查看自己的web服务器的状态。这个时候就可以选择net/http/pprof。你只需要引入包_”net/http/pprof”

        go func() {
            http.ListenAndServe("localhost:8090", nil)
        }()

go run main.go 后就可以在浏览器中使用http://localhost:8080/debug/pprof/直接看到当前web服务的状态,包括CPU占用情况和内存使用情况等,例如: 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值