Golang程序诊断

简介

Go生态系统提供了一整套用于诊断Go程序中的逻辑和性能问题的API和工具。该篇文章总结了可用的工具,并帮助Go用户针对他们的特定问题选择合适的工具。html

诊断解决方案能够分为如下几类:前端

  • Profiling: Profiling工具分析Go程序的复杂性和成本,例如其内存使用状况和常常调用的函数,以识别Go程序的最耗费资源的部分。
  • Tracing: Tracing是一种工具代码,能够在调用或用户请求的整个生命周期中分析延迟。跟踪提供了每一个组件对系统整体延迟形成多少延迟的概述。跟踪能够跨越多个Go进程。
  • Debugging: Debugging容许咱们暂停Go程序并检查其执行。程序状态和流程能够经过调试来验证。
  • Runtime statistics and events: 运行时状态和事件的收集和分析提供了Go程序运行情况的高级概述。指标的峰/谷有助于咱们识别吞吐量,利用率和性能的变化。

注意:某些诊断工具可能会相互干扰。例如,内存分析会影响CPU profiles,goroutine 阻塞分析会影响调度程序跟踪。一次只使用一种工具以得到更精确的信息。git

Profiling

分析对于识别消耗资源严重或常常调用的代码段颇有用。 Go运行时以pprof可视化工具所需的格式提供概要分析数据。能够在测试过程当中经过go test或 net/http/pprof 程序包中提供的端点收集性能分析数据。用户须要收集性能分析数据并使用pprof工具来过滤和可视化代码路径。github

runtime/pprof 软件包提供的预约义profiles:golang

  • cpu: CPU profile肯定了程序在真实消耗CPU时间片(而不是在睡眠或等待I/O)所花费的时间。
  • heap: heap profile报告内存分配样本;用于监视当前和历史内存使用状况,并检查内存泄漏。
  • threadcreate: 线程建立profile报告了致使建立新OS线程的程序部分。
  • goroutine: Goroutine profile报告全部当前goroutine的堆栈跟踪。
  • block: Block profile显示goroutine在哪里阻塞同步原语(包括计时器通道)的等待。默认状况下未启用Block profile;使用runtime.SetBlockProfileRate启用它。
  • mutex: Mutex profile报告锁争用。若是您认为因为互斥锁争用而没法充分利用CPU,请使用此profile。Mutex profile默认状况下未启用,请参见runtime.SetMutexProfileFraction启用它。

我还可使用哪些其余分析器来分析Go程序?web

在Linux上,可使用perf工具来分析Go程序。 Perf能够分析和解散cgo /SWIG代码和内核,所以深刻了解本机/内核性能瓶颈可能颇有用。在macOS上,Instruments套件可用于profile Go程序。数据库

我能够profile个人生产服务吗?编程

是。在生产环境中对程序进行概要分析是安全的,可是启用某些概要文件(例​​如CPU概要文件)会增长成本。您应该指望看到性能降低。能够经过在生产以前打开Profiler来测量Profiler的开销来估算性能损失。后端

您可能须要按期分析生产服务。特别是在具备单个进程多个副本的系统中,按期选择随机副本是一个安全的选择。选择一个生产过程,每隔Y秒将其概要分析X秒,并保存结果以进行可视化和分析;而后按期重复。能够手动或自动查看结果以发现问题。profile的收集可能会相互干扰,所以建议一次仅收集一个profile。安全

可视化分析数据的最佳方法是什么?
Go工具使用go tool pprof提供profile数据的文本,图形和callgrind可视化。阅读Profiling Go程序以查看它们的运行状况。

以文本形式列出最消耗资源的调用
pprof-text.png

将最消耗资源的调用可视化为图表
pprof-dot.png

Weblist视图在HTML页面中逐行显示源代码最消耗资源的部分。在下面的示例中,运行时花费了530毫秒。runtime.concatstrings和清单中列出了每行的成本。

将最消耗资源的调用可视化为weblist
pprof-weblist.png

可视化轮廓数据的另外一种方法是火焰图。火焰图使您能够在特定的祖先路径中移动,所以能够放大/缩小代码的特定部分。上游pprof支持火焰图。

flame.png

我是否限于内置profiles?

除了运行时提供的功能外,Go用户还能够经过pprof.Profile建立其自定义profile,并使用现有工具进行检查。

我能够在其余路径和端口上提供事件探查器处理程序(/debug/pprof/ ...)吗?

是。 net/http/pprof软件包默认状况下将其处理程序注册到默认的多路复用器,可是您也可使用从软件包中导出的处理程序本身注册它们。

例如,如下示例将在 /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))
}

Tracing

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

Tracing 能够在如下方面帮助到咱们:

  • 在Go流程中检测和分析应用程序延迟。
  • 在一连串的通话中衡量特定通话的费用。
  • 找出利用率和性能改进。若是不跟踪数据,瓶颈并不老是很明显。

在单片系统中,从程序的构建块中收集诊断数据相对容易。全部模块都生活在一个过程当中,并共享公共资源来报告日志,错误和其余诊断信息。一旦您的系统超出了单个进程并开始分布,就很难跟踪从前端Web服务器到其全部后端的调用,直到将响应返回给用户为止。在这里,分布式跟踪对仪器和分析生产系统起着重要做用。

分布式跟踪是一种工具代码,能够分析用户请求整个生命周期中的延迟。当系统是分布式的而且常规的性能分析和调试工具没法扩展时,您可能但愿使用分布式跟踪工具来分析用户请求和RPC的性能。

分布式跟踪在如下几个方面能够帮助到咱们:

  • 大型系统中的仪器和配置文件应用程序延迟。
  • 跟踪用户请求生命周期内的全部RPC,并查​​看仅在生产中可见的集成问题。
  • 找出能够应用于咱们的系统的性能改进。在收集跟踪数据以前,许多瓶颈并不明显。

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

有没有一种方法能够自动拦截每一个函数调用并建立跟踪?

Go没法提供一种自动拦截每一个函数调用并建立跟踪范围的方法。您须要手动检测代码以建立,结束和注释跨度。

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

您能够在context.Context中传播跟踪标识符和标记。业界尚无规范的跟踪键或跟踪头的通用表示形式。每一个跟踪提供者都有责任在其Go库中提供传播实用程序。

标准库或运行时中还有哪些其余低级事件能够包含在跟踪中?

标准库和运行时正尝试公开一些其余API,以在发生低级内部事件时进行通知。例如,httptrace.ClientTrace提供API以在传出请求的生命周期中跟踪低级事件。正在进行中的工做是从运行时执行跟踪器中检索低级运行时事件,并容许用户定义和记录其用户事件。

Debugging

调试是肯定程序行为异常的过程。调试器使咱们可以了解程序的执行流程和当前状态。调试有几种样式。本节仅关注将调试器附加到程序和core dump调试。

Go用户一般使用如下调试器:

  • Delve: Delve是Go编程语言的调试器。它支持Go的运行时概念和内置类型。 Delve试图成为Go程序的全功能可靠调试器。
  • GDB: Go经过标准的Go编译器和Gccgo提供了GDB支持。堆栈管理,线程和运行时所包含的方面与执行模型有很大不一样,GDB但愿它们会混淆调试器,即便使用gccgo编译程序也是如此。即便可使用GDB调试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程序进行过后调试吗?

core dump文件是一个文件,其中包含正在运行的进程的内存dump及其进程状态。它主要用于程序的过后调试,并在程序仍在运行时了解其状态。这两种状况使core dump的调试成为进行过后分析和分析生产服务的良好诊断工具。能够从Go程序中获取core文件,并使用delve或gdb进行调试,有关逐步指南,请参见core dump调试页面。

Runtime statistics and events

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

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

  • runtime.ReadMemStats报告与堆分配和垃圾收集相关的指标。内存统计信息对于监视进程正在消耗多少内存资源,进程是否能够充分利用内存以及捕获内存泄漏颇有用。
  • debug.ReadGCStats读取有关垃圾收集的统计信息。查看在GC暂停上花费了多少资源是颇有用的。它还报告了垃圾收集器暂停和暂停时间百分比的时间线。
  • debug.Stack返回当前的堆栈跟踪。堆栈跟踪对于查看当前正在运行多少个goroutine,它们在作什么以及是否被阻塞很是有用。
  • debug.WriteHeapDump暂停全部goroutine的执行,并容许您将堆dump到文件中。堆dump是给定时间Go进程内存的快照。它包含全部分配的对象以及goroutine,finalizer等。
  • runtime.NumGoroutine返回当前goroutine的数量。能够监视该值以查看是否使用了足够的goroutine,或者检测goroutine泄漏。

Execution tracer

Go附带了一个运行时执行跟踪程序,以捕获各类运行时事件。调度,系统调用,垃圾回收,堆大小和其余事件由运行时收集,而且能够经过go工具跟踪进行可视化。执行跟踪器是检测延迟和利用率问题的工具。您能够检查CPU的利用率,以及什么时候网络链接或系统调用会致使goroutine抢占。

跟踪器在一下几个方面能够帮助到您:

  • 了解您的goroutine如何执行。
  • 了解一些核心运行时事件,例如GC运行。
  • 肯定并行化执行不佳。

可是,这对于识别热点(例如分析过多的内存或CPU使用率的缘由)不是很好。而是首先使用分析工具来解决它们。

tracer-lock.png

上面的go工具跟踪可视化显示执行开始良好,而后序列化了。它代表可能存在致使瓶颈的共享资源的锁争用。

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

GODEBUG

若是相应地设置了GODEBUG环境变量,则运行时还会发出事件和信息。

  • GODEBUG=gctrace=1 在每一个收集时打印垃圾收集器事件,总结收集的内存量和暂停的时间。
  • GODEBUG=schedtrace=X 每X毫秒打印一次调度事件。

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

  • GODEBUG=cpu.all=off 禁用全部可选指令集扩展的使用。
  • GODEBUG=cpu.extension=off 禁止使用指定指令集扩展中的指令。 扩展名是指令集扩展名的小写名称,例如sse41或avx。
相关文章
相关标签/搜索