c++通过dbg分析内存和cpu,可能大家都会。本篇主要分析通过delve分析golang程序cpu占用高的问题。
delve是golang推荐的专门go语言调试工具,用来替代gdb。golang组织说delve能更好的理解go语言。
先用vscode写个简单的demo,main.go代码如下:
package main
import (
"fmt"
"os"
"os/signal"
)
func main() {
fmt.Println("main start")
msgList := make(chan int, 100)
go func() {
for {
select {
case <-msgList:
default:
}
}
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
s := <-c
fmt.Println("main exit.get signal:", s)
}
go build -o cpudetect main.go编译生成cpudetect并放到服务器运行。
接下来是具体的分析步骤:
top
Tasks: 168 total, 1 running, 166 sleeping, 1 stopped, 0 zombie
%Cpu(s): 94.4 us, 5.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 995684 total, 73064 free, 613904 used, 308716 buff/cache
KiB Swap: 2097148 total, 2093300 free, 3848 used. 200060 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7819 root 20 0 703020 1000 632 S 94.1 0.1 3:07.05 cpudetect
1 root 20 0 128296 6704 3912 S 0.0 0.7 0:04.22 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
4 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
可以看到cpudetect进程的cpu占用了94.1。
./dlv attach 7819
top - 01:36:29 up 3:18, 2 users, load average: 0.68, 0.18, 0.18
Threads: 5 total, 1 running, 4 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 995684 total, 73188 free, 613780 used, 308716 buff/cache
KiB Swap: 2097148 total, 2093300 free, 3848 used. 200184 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7823 root 20 0 703020 1000 632 R 99.3 0.1 0:50.62 cpudetect
7819 root 20 0 703020 1000 632 S 0.0 0.1 0:00.00 cpudetect
7820 root 20 0 703020 1000 632 S 0.0 0.1 0:00.06 cpudetect
7821 root 20 0 703020 1000 632 S 0.0 0.1 0:00.01 cpudetect
7822 root 20 0 703020 1000 632 S 0.0 0.1 0:00.00 cpudetect
可以看到主要由于线程7823占用cpu过高导致。
./dlv attach 7819
#使用goroutines命令查看所有的协程信息:
(dlv) goroutines
Goroutine 1 - User: E:/golang/cpuDetect/src/cpudetect/main.go:26 main.main (0x494212)
Goroutine 2 - User: c:/go/src/runtime/proc.go:305 runtime.gopark (0x431ae0)
Goroutine 3 - User: c:/go/src/runtime/proc.go:305 runtime.gopark (0x431ae0)
Goroutine 4 - User: c:/go/src/runtime/proc.go:305 runtime.gopark (0x431ae0)
Goroutine 5 - User: c:/go/src/runtime/proc.go:305 runtime.gopark (0x431ae0)
Goroutine 6 - User: E:/golang/cpuDetect/src/cpudetect/main.go:16 main.main.func1 (0x4942e4) (thread 7823)
Goroutine 7 - User: c:/go/src/runtime/sigqueue.go:147 os/signal.signal_recv (0x44559c)
Goroutine 8 - User: c:/go/src/runtime/proc.go:305 runtime.gopark (0x431ae0)
可以看到协程6挂在7823的线程下(7823是刚才cpu占用高的线程)。
#切换到协程6并查看具体堆栈信息:
(dlv) goroutine 6
Switched from 6 to 6 (thread 7823)
(dlv) bt
0 0x0000000000405a89 in runtime.chanrecv
at c:/go/src/runtime/chan.go:451
1 0x0000000000405dca in runtime.selectnbrecv
at c:/go/src/runtime/chan.go:646
2 0x00000000004942e4 in main.main.func1
at E:/golang/cpuDetect/src/cpudetect/main.go:16
3 0x000000000045dad1 in runtime.goexit
at c:/go/src/runtime/asm_amd64.s:1373
从上面的堆栈信息,可以定位到main.go 16行,再分析对应的代码,就可以确定是select中加了defualt但没有任何处理导致cpu空转,从而导致cpu过高。
另外,dlv调试还有很多其他的命令,大家自己通过help命令查看吧。