调试
debug
- gdb:
- Gccgo 原生支持 gdb,因此可以用 gdb 调试 Go 语言代码,但 dlv 对 Go 语言 debug 的支持比 gdb 更好
- Gdb 对 Go 语言的栈管理,多线程支持等方面做的不够好,调试代码时可能有错乱现象
- dlv:
- Go 语言的专有 debugger
dlv 的配置
- 配置
- 在 vscode 中配置 dlb
- 菜单:View -> Command Palette
- 选择 Go : Install/Update Tools,选择安装
- 安装完后,从改入口列表中可以看到 dlv 和 dlv-dap 已经安装好
- Debug 方法
- 在代码中设置断点
- 菜单中选择 Run -> Start Debugging 即可进入调试
更多 debug 方法
- 添加日志
- 在关键代码分支中加入日志
- 基于fmt包将日志输出到标准输出 stdout
- fmt.Println()
- fmt 无日志重定向,无日志分级
- 即与日志框架将日志输出到对应的 appender
- 比如可利用 glog 进行日志输出
- 可配置 appender,将标准输出转至文件
- 支持多级日志输出,可修改配置调整日志等级
- 自带时间戳和代码行,方便调试
- 比如可利用 glog 进行日志输出
Glog 使用方法示例
import (
"flag"
"github.com/golang/glog"
)
func main() {
flag.Set("v", "4")
glog.V(2).Info("Starting http server...")
mux := http.NewServeMux()
mux.HandleFunc("/", rootHandler)
err := http.ListenAndServe(":80", mux)
if err != nil {
log.Fatal(err)
}
}
性能分析(Performance Profiling)
- CPU Profiling: 在代码中添加 CPUProfile 代码,runtime/pprof 包提供支持
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
}
分析 CPU 瓶颈
- 运行 cpuprofilie 代码后,会在 /tmp/cpuprofile 中记录 cpu 使用时间
- 运行 go tool pprof /tmp/cpuprofile 进入分析模式
- 运行 top10 查看 top 10线程,显示 30ms 花费在 main.main
Showing nodes accounting for 30ms, 100% of 30ms total
flat flat% sum% cum cum%
30ms 100% 100% 30ms 100% main.main
0 0% 100% 30ms 100% runtime.main
- (pprof) list main.main 显示 30 毫秒都花费在循环上
Total: 30ms
30ms 30ms (flat, cum) 100% of Total
20ms 20ms 21: for i := 0; i < 100000000; i++ {
10ms 10ms 22: result += I
- 可执行 web 命令生成 svg 文件,在通过浏览器打开 svg 文件查看图形化分析结果
其他可用 profiling 工具分析的问题
- CPU profile
- 程序的 CPU 使用情况,每 100 毫秒采集一次 CPU 使用情况
- Memory Profile
- 程序的内存使用情况
- Block Profiling
- 非运行态的 goroutine 细节,分析和查找死锁
- Goroutine Profiling
- 所有 goroutines 的细节状态,有哪些 goroutine,它们的调用关系是怎样的
针对 http 服务的 pprof
- net/http/pprof 包提供支持
- 如果采用默认 mux handle,则只需 import _ "net/http/pprof"
- 如果采用自定义 mux handle,则需要注册 pprof handler
import (
"net/http/pprof"
)
func startHTTP(addr string, s *tnetd.Server) {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
server := &http.Server{
Addr: addr,
Handler: mux,
}
server.ListenAndServe()
}
分析 go profiling 结果
在运行了开启 pprof 的服务器以后,可以通过访问对应的 URL 获得 profile 结果
- allocs: A sampling of all past memory allocations
- block: Stack traces that led to blocking on synchronization primitives
- cmdline: The command line invocation of the current program
- goroutine: Stack traces of all current goroutines
- heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
- mutex: Stack traces of holders of contended mutexes
- profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
- threadcreate: Stack traces that led to the creation of new OS threads
- trace: A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
结果分析示例
- 分析 goroutine 运行情况
- curl localhost/debug/pprof/goroutine?debug=2
- 分析堆内存使用情况
- curl localhost/debug/pprof/heap?debug=2