go包:pprof调试器的使用

2023-07-05  本文已影响0人  呦丶耍脾气

1. 调用图说明

1.1 示例图

image.png

1.2 节点颜色

1.3 节点字体大小

1.4 边框颜色

1.5 箭头大小

1.6 箭头类型

2. 火焰图说明

2.1 示例图

image.png

2.2 含义说明

**排查三步曲: **top: 根据资源排序; list:查看具体代码信息;web:在浏览器中打开调用图

3. 排查内存占用高

3.1 进入交互式

# 这里的app是编译后的文件名
➜ go tool pprof app http://127.0.0.1:6060/debug/pprof/heap
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/heap
Saved profile in /Users/liuqh/pprof/pprof.app.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
File: app
Type: inuse_space
Time: Nov 16, 2021 at 9:14pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)

3.2 定位内存最高

(pprof) top
Showing nodes accounting for 5290.49kB, 100% of 5290.49kB total
      flat  flat%   sum%        cum   cum%
 3754.47kB 70.97% 70.97%  5290.49kB   100%  52lu/go-pprof/scenes.UseHeapDemo.func1
 1536.02kB 29.03%   100%  1536.02kB 29.03%  fmt.Sprintf
         0     0%   100%   512.01kB  9.68%  52lu/go-pprof/scenes.getBody
         0     0%   100%  1024.02kB 19.36%  52lu/go-pprof/scenes.getGirl

3.3 查看具体代码

(pprof) list UseHeapDemo
Total: 5.17MB
ROUTINE ======================== 52lu/go-pprof/scenes.UseHeapDemo.func1 in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/heap.go
    3.67MB     5.17MB (flat, cum)   100% of Total
         .          .      8:func UseHeapDemo()  {
         .          .      9:    go func() {
         .          .     10:        var result []string
         .          .     11:        i := 1
         .          .     12:        for {
         .   512.01kB     13:            body := getBody()
         .        1MB     14:            girl := getGirl()
         .          .     15:            result = append(result,body...)
    3.67MB     3.67MB     16:            result = append(result,girl...)
         .          .     17:            fmt.Printf("第%d轮,count:%v \n",i,len(result))
         .          .     18:            time.Sleep(time.Second * 5)
         .          .     19:            i++
         .          .     20:        }
         .          .     21:    }()
(pprof)

3.4 在浏览器中查看

(pprof) web

4. 排查CPU占用高

4.1 进入交互式

# 这里的app是编译后的文件名
➜ go tool pprof app http://127.0.0.1:6060/debug/pprof/profile
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/profile
Saved profile in /Users/liuqh/pprof/pprof.app.samples.cpu.003.pb.gz
File: app
Type: cpu
Time: Nov 16, 2021 at 8:55pm (CST)
Duration: 30s, Total samples = 15.76s (52.53%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

4.2 定位cpu占用最高

(pprof) top
Showing nodes accounting for 15.68s, 99.49% of 15.76s total 
Dropped 9 nodes (cum <= 0.08s)
flat  flat%   sum%        cum   cum%
15.44s 97.97% 97.97%     15.68s 99.49%  52lu/go-pprof/scenes.compute
0.24s  1.52% 99.49%      0.24s  1.52%  runtime.asyncPreempt
0     0% 99.49%     15.68s 99.49%  52lu/go-pprof/scenes.UseCpuDemo (inline)
0     0% 99.49%     15.68s 99.49%  main.main
0     0% 99.49%      0.08s  0.51%  runtime.findrunnable
0     0% 99.49%     15.68s 99.49%  runtime.main
0     0% 99.49%      0.08s  0.51%  runtime.mcall
0     0% 99.49%      0.08s  0.51%  runtime.park_m
0     0% 99.49%      0.08s  0.51%  runtime.schedule

4.3 查看具体代码

(pprof) list compute
Total: 15.76s
ROUTINE ======================== 52lu/go-pprof/scenes.compute in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/cpu.go
    15.44s     15.68s (flat, cum) 99.49% of Total
         .          .      6:    compute()
         .          .      7:}
         .          .      8:
         .          .      9:func compute()  {
         .          .     10:    var total int64
    15.44s     15.68s     11:    for i := 0; i < 100000000000; i++ {
         .          .     12:        total = total + int64(i)
         .          .     13:    }
         .          .     14:    fmt.Println("遍历完成")
         .          .     15:}

4.4 在浏览器中查看

(pprof) web

@注:需要安装graphviz

5. 排查协程泄露

分析协程栈有两方面的作用,一是查看协程的数量,查看协程是否泄漏。二是查看当前大量的协程在执行哪些函数,判断当前协程是否健康。

5.1 什么是协程泄露

Go的并发是以goroutinechannel的形式实现的。协程泄露是指goroutine创建后,长时间得不到释放,并且还在不断地创建新的goroutine协程,最终导致内存耗尽,程序崩溃。

5.2 进入交互式

➜ go tool pprof app http://127.0.0.1:6060/debug/pprof/goroutine
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/goroutine
Saved profile in /Users/liuqh/pprof/pprof.app.goroutine.007.pb.gz
File: app
Type: goroutine
Time: Nov 16, 2021 at 9:43pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)

5.3 定位协程情况

(pprof) top
Showing nodes accounting for 37, 100% of 37 total # 收集到37个协程
Showing top 10 nodes out of 41
      flat  flat%   sum%        cum   cum%
        35 94.59% 94.59%         35 94.59%  runtime.gopark
         1  2.70% 97.30%          1  2.70%  net/http.(*connReader).backgroundRead
         1  2.70%   100%          1  2.70%  runtime/pprof.runtime_goroutineProfileWithLabels
         0     0%   100%          1  2.70%  52lu/go-pprof/scenes.UseGoroutineDemo
         0     0%   100%          1  2.70%  52lu/go-pprof/scenes.errorUse
         0     0%   100%         30 81.08%  52lu/go-pprof/scenes.errorUse.func1
         0     0%   100%          3  8.11%  bufio.(*Reader).ReadLine
         0     0%   100%          3  8.11%  bufio.(*Reader).ReadSlice
         0     0%   100%          3  8.11%  bufio.(*Reader).fill
         0     0%   100%          1  2.70%  internal/poll.(*FD).Accep

分析:

从上面看到有35个协程都在runtime.gopark函数,runtime.gopark:代表协程处于休眠状态,其中有30个协程是由scenes.errorUse.func1产出。

5.4 查看具体代码

(pprof) list errorUse.func1
Total: 37
ROUTINE ======================== 52lu/go-pprof/scenes.errorUse.func1 in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/goroutine.go
         0         30 (flat, cum) 81.08% of Total
         .          .      9:
         .          .     10:func errorUse()  {
         .          .     11:    for  {
         .          .     12:        time.Sleep(time.Second)
         .          .     13:        go func() {
         .         30     14:            time.Sleep(time.Second * 30)
         .          .     15:        }()
         .          .     16:    }
         .          .     17:}
         .          .     18:func trueUse()  {
         .          .     19:    res := make(chan []int)

5.5 在浏览器中查看

(pprof) web

5.6 base基准分析

除了上面的分析方法,pprof还提供了更强大的工具用于对比前后特征文件的不同。下例使用了-base标志,后跟基准特征文件。

# 使用基准值
➜  go tool pprof -base pprof.app.goroutine.001.pb.gz pprof.app.goroutine.002.pb.gz
File: app
Type: goroutine
Time: Nov 16, 2021 at 10:12pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 28, 100% of 28 total
      flat  flat%   sum%        cum   cum%
        28   100%   100%         28   100%  runtime.gopark
         0     0%   100%         28   100%  52lu/go-pprof/scenes.errorUse.func1
         0     0%   100%         28   100%  time.Sleep
(pprof)➜  pprof go tool pprof -base pprof.app.goroutine.001.pb.gz pprof.app.goroutine.002.pb.gz
File: app
Type: goroutine
Time: Nov 16, 2021 at 10:12pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 28, 100% of 28 total
      flat  flat%   sum%        cum   cum%
        28   100%   100%         28   100%  runtime.gopark
         0     0%   100%         28   100%  52lu/go-pprof/scenes.errorUse.func1
         0     0%   100%         28   100%  time.Sleep

从上面可以看出,后一个基准特征文件比前一个基准特征文件多了28个协程,其中28个协程都处于执行runtime.gopark阶段,从而可以判断协程是否已经泄漏。

6. 排查锁竞争情况

mutex主要用于查看锁争用导致的休眠时间,这有助于排查由于锁争用导致CPU利用率不足的问题,需要注意的是:默认不开启锁的竞争分析,需要调用runtime.SetMutexProfileFraction进行设置。开启方式如下:

func init() {
    // 开启http端口,用协程的方式监听,否则会阻塞
    go func() {
        if err := http.ListenAndServe(":6060", nil); err != nil {
            fmt.Println("pprof err:",err)
        }
    }()
  // 开启锁争用分析
    // 默认不开启锁的竞争分析,需要调用runtime.SetMutexProfileFraction进行设置。
    runtime.SetMutexProfileFraction(1)
}

6.1 进入交互式

➜ go tool pprof http://127.0.0.1:6060/debug/pprof/mutex
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/mutex
Saved profile in /Users/liuqh/pprof/pprof.contentions.delay.007.pb.gz
Type: delay
Time: Nov 17, 2021 at 3:04pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)

6.2 排序互斥耗时

(pprof) top
Showing nodes accounting for 37.09s, 100% of 37.09s total
      flat  flat%   sum%        cum   cum%
    37.09s   100%   100%     37.09s   100%  sync.(*Mutex).Unlock
         0     0%   100%     37.09s   100%  52lu/go-pprof/scenes.UseMutexDemo.func1

由上面可以看出,锁争用集中在互斥锁中,互斥带来的休眠时间为37.09s

6.3 查看具体代码

# 查看具体函数
(pprof) list UseMutexDemo
Total: 37.09s
ROUTINE ======================== 52lu/go-pprof/scenes.UseMutexDemo.func1 in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/mutex.go
         0     37.09s (flat, cum)   100% of Total
         .          .     21:            stu.idList = append(stu.idList, id)
         .          .     22:            time.Sleep(time.Millisecond * 200)
         .          .     23:        }()
         .          .     24:    }
         .          .     25:}

6.4 在浏览器中查看

(pprof) web
image.png

7. 排查阻塞操作

在程序中,除了锁的争用会导致阻塞之外,很多逻辑都会导致阻塞,比如网络请求超时、IO阻塞等。需要注意的是:默认不开启阻塞分析,需要调用runtime.SetMutexProfileFraction进行设置。开启方式如下:

func init() {
    ...
    // 开启block分析
    runtime.SetBlockProfileRate(1)
}

7.1 查看/debug/pprof

1. 当前阻塞数量

image.png

2. 具体阻塞位置

image.png

7.2 进入交互式

➜ go tool pprof http://127.0.0.1:6060/debug/pprof/block
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/block
Saved profile in /Users/liuqh/pprof/pprof.contentions.delay.012.pb.gz
Type: delay
Time: Nov 17, 2021 at 4:30pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)

7.3 排序阻塞时间

(pprof) top
Showing nodes accounting for 59.75s, 100% of 59.75s total
Dropped 4 nodes (cum <= 0.30s)
      flat  flat%   sum%        cum   cum%
    59.75s   100%   100%     59.75s   100%  runtime.chanrecv1
         0     0%   100%     59.75s   100%  52lu/go-pprof/scenes.UseBlockDemo (inline)
         0     0%   100%     59.75s   100%  52lu/go-pprof/scenes.handle
         0     0%   100%     59.75s   100%  main.main
         0     0%   100%     59.75s   100%  runtime.main

7.4 查看具体阻塞代码

# 查看UseBlockDemo函数,发现内部调用函数handle,且handle函数耗时59.75s
(pprof) list Use
Total: 59.75s
ROUTINE ======================== 52lu/go-pprof/scenes.UseBlockDemo in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/block.go
         0     59.75s (flat, cum)   100% of Total
         .          .      6:var sqlCh = make(chan []string)
         .          .      7:var bCh = make(chan bool)
         .          .      8:
         .          .      9:func UseBlockDemo() {
         .          .     10:    for i := 0; i < 1000; i++ {
         .     59.75s     11:        handle()
         .          .     12:    }
         .          .     13:}
         .          .     14:
         .          .     15:func handle()  {
         .          .     16:    // 等待文件写入
# 查看handle
(pprof) list handle
Total: 59.75s
ROUTINE ======================== 52lu/go-pprof/scenes.handle in /Users/liuqh/ProjectItem/GoItem/go-pprof/scenes/block.go
         0     59.75s (flat, cum)   100% of Total
         .          .     16:    // 等待文件写入
         .          .     17:    go func() {
         .          .     18:        time.Sleep(time.Second)
         .          .     19:        bCh <- true
         .          .     20:    }()
         .     35.08s     21:    <-bCh
         .          .     22:    // 发起网络请求
         .          .     23:    go func() {
         .          .     24:        time.Sleep(time.Millisecond * 500)
         .          .     25:        reqCh <- "req success"
         .          .     26:    }()
         .          .     27:    // 阻塞等待
         .     17.59s     28:    <-reqCh
         .          .     29:    // 执行SQL查询
         .          .     30:    go func() {
         .          .     31:        time.Sleep(time.Millisecond * 200)
         .          .     32:        sqlCh <- []string{"hello","world"}
         .          .     33:    }()
         .      7.08s     34:    <-sqlCh
         .          .     35:}
         .          .     36:

7.5 在浏览器中查看

上一篇下一篇

猜你喜欢

热点阅读