诊断

简介

Go 生态系统提供了一套用于诊断 Go 程序中的逻辑和性能问题的 API 和工具。此页面总结了可用的工具,并帮助 Go 用户为其特定问题选择合适的工具。

诊断解决方案可归类为以下几组

注意:某些诊断工具可能会相互干扰。例如,精确的内存分析会影响 CPU 分析,而 goroutine 阻塞分析会影响调度程序跟踪。单独使用工具以获取更准确的信息。

分析

分析对于识别代价高昂或经常调用的代码部分非常有用。Go 运行时提供 分析数据,其格式符合 pprof 可视化工具 的预期格式。分析数据可以通过 go test net/http/pprof 包提供的端点在测试期间收集。用户需要收集分析数据并使用 pprof 工具过滤和可视化最重要的代码路径。

runtime/pprof 包提供的预定义分析

我可以使用哪些其他分析器来分析 Go 程序?

在 Linux 上,perf 工具可用于分析 Go 程序。Perf 可以分析和展开 cgo/SWIG 代码和内核,因此它有助于深入了解本机/内核性能瓶颈。在 macOS 上,Instruments 套件可用于分析 Go 程序。

我可以分析我的生产服务吗?

可以。在生产中分析程序是安全的,但启用某些分析(例如 CPU 分析)会增加成本。你应该预见到性能下降。可以通过在生产中启用分析器之前测量其开销来估计性能损失。

你可能希望定期分析你的生产服务。尤其是在具有单个进程的多个副本的系统中,定期选择一个随机副本是一个安全的选择。选择一个生产进程,每隔 Y 秒分析它 X 秒,并将结果保存下来以进行可视化和分析;然后定期重复。可以手动和/或自动查看结果以查找问题。收集分析可能会相互干扰,因此建议一次只收集一个分析。

可视化分析数据的最佳方法是什么?

Go 工具使用 go tool pprof 提供分析数据的文本、图形和 callgrind 可视化。阅读 分析 Go 程序 以了解它们的作用。


以文本形式列出最昂贵的调用。


以图形形式可视化最昂贵的调用。

Weblist 视图以 HTML 页面逐行显示源代码的昂贵部分。在以下示例中,530 毫秒花在 runtime.concatstrings 中,并且在列表中显示了每行的成本。


以 Weblist 形式可视化最昂贵的调用。

可视化分析数据的另一种方法是 火焰图。火焰图允许你在特定的祖先路径中移动,以便你可以放大/缩小代码的特定部分。上游 pprof 支持火焰图。


火焰图提供可视化以找出最昂贵的代码路径。

我是否仅限于内置分析?

除了运行时提供的功能之外,Go 用户还可以通过 pprof.Profile 创建自己的自定义分析,并使用现有工具来检查它们。

我可以在不同的路径和端口上提供分析器处理程序 (/debug/pprof/...) 吗?

可以。默认情况下,net/http/pprof 包将其处理程序注册到默认的 mux,但你也可以使用该包导出的处理程序自己注册它们。

例如,以下示例将在 /custom_debug_path/profile 上的 :7777 处提供 pprof.Profile 处理程序

package main

import (
	"log"
	"net/http"
	"net/http/pprof"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/custom_debug_path/profile", pprof.Profile)
	log.Fatal(http.ListenAndServe(":7777", mux))
}

跟踪

跟踪是一种对代码进行检测的方法,用于分析调用链整个生命周期内的延迟。Go 提供 golang.org/x/net/trace 包作为每个 Go 节点的最小跟踪后端,并提供一个带有简单仪表板的最小检测库。Go 还提供一个执行跟踪器来跟踪一个时间间隔内的运行时事件。

跟踪使我们能够

在单体系统中,从程序的构建模块收集诊断数据相对容易。所有模块都存在于一个进程中,并共享公共资源来报告日志、错误和其他诊断信息。一旦您的系统超出单个进程并开始分布,就很难跟踪从前端 Web 服务器到其所有后端的调用,直到向用户返回响应。这是分布式跟踪在检测和分析您的生产系统中发挥重要作用的地方。

分布式跟踪是一种检测代码的方法,用于分析用户请求整个生命周期内的延迟。当系统分布且传统分析和调试工具无法扩展时,您可能需要使用分布式跟踪工具来分析用户请求和 RPC 的性能。

分布式跟踪使我们能够

Go 生态系统为每个跟踪系统和与后端无关的系统提供各种分布式跟踪库。

有没有办法自动拦截每个函数调用并创建跟踪?

Go 不提供自动拦截每个函数调用并创建跟踪跨度的办法。您需要手动检测您的代码以创建、结束和注释跨度。

我应该如何在 Go 库中传播跟踪头?

您可以在 context.Context 中传播跟踪标识符和标记。业界还没有规范的跟踪键或跟踪头部的通用表示形式。每个跟踪提供程序负责在各自的 Go 库中提供传播实用程序。

标准库或运行时中还有哪些低级别事件可以包含在跟踪中?

标准库和运行时正在尝试公开几个附加 API 来通知低级别内部事件。例如,httptrace.ClientTrace 提供 API 来跟踪传出请求生命周期中的低级别事件。目前正在进行一项工作,从运行时执行跟踪器中检索低级别运行时事件,并允许用户定义和记录其用户事件。

调试

调试是识别程序行为不当原因的过程。调试器让我们了解程序的执行流程和当前状态。有几种调试风格;本节只关注将调试器附加到程序和核心转储调试。

Go 用户主要使用以下调试器

调试器与 Go 程序配合得如何?

gc 编译器执行优化,例如函数内联和变量寄存器化。这些优化有时会让调试器调试变得更困难。目前正在进行一项工作,以提高为优化二进制文件生成的 DWARF 信息的质量。在这些改进可用之前,我们建议在构建要调试的代码时禁用优化。以下命令构建了一个没有编译器优化的包

$ go build -gcflags=all="-N -l"

作为改进工作的一部分,Go 1.10 引入了新的编译器标志 -dwarflocationlists。该标志导致编译器添加位置列表,帮助调试器处理优化二进制文件。以下命令构建了一个带有优化但带有 DWARF 位置列表的包

$ go build -gcflags="-dwarflocationlists=true"

推荐的调试器用户界面是什么?

即使 delve 和 gdb 都提供 CLI,大多数编辑器集成和 IDE 都提供特定于调试的用户界面。

是否可以在 Go 程序中进行事后调试?

核心转储文件是一个包含正在运行进程的内存转储及其进程状态的文件。它主要用于程序的事后调试,并了解其在运行时的状态。这两种情况使核心转储调试成为事后和分析生产服务的有用诊断辅助工具。可以从 Go 程序获取核心文件并使用 delve 或 gdb 进行调试,请参阅 核心转储调试 页面,了解分步指南。

运行时统计信息和事件

运行时为用户提供统计信息和内部事件报告,以便在运行时级别诊断性能和利用率问题。

用户可以监视这些统计信息,以便更好地了解 Go 程序的整体运行状况和性能。一些经常监视的统计信息和状态

执行跟踪器

Go 附带一个运行时执行跟踪器,用于捕获各种运行时事件。调度、系统调用、垃圾回收、堆大小和其他事件由运行时收集,并且可以由 go 工具跟踪进行可视化。执行跟踪器是一个检测延迟和利用率问题的工具。你可以检查 CPU 利用率如何,以及何时网络或系统调用导致 goroutine 被抢占。

跟踪器可用于

但是,它不适合识别热点,例如分析过度内存或 CPU 使用的原因。相反,请首先使用分析工具来解决这些问题。

上面,go 工具跟踪可视化显示执行开始良好,然后它变得序列化。它表明可能存在对创建瓶颈的共享资源的锁争用。

请参阅 go tool trace 以收集和分析运行时跟踪。

GODEBUG

如果 GODEBUG 环境变量相应设置,运行时还会发出事件和信息。

GODEBUG 环境变量可用于禁用标准库和运行时中指令集扩展的使用。