You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

643 lines
43 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-cn" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Go 内置分析工具 # 这一章节将介绍Go 内置分析工具。通过这些工具我们可以分析、诊断、跟踪竞态GMP调度CPU耗用等问题。
go build # go build命令用来编译Go 程序。go build重要的命令行选项有以下几个
go build -n # -n选项用来显示编译过程中所有执行的命令不会真正执行。通过该选项我们可以查看编译器连接器如何工作的
# # _/home/vagrant/dive-into-go # mkdir -p $WORK/b001/ cat &gt;$WORK/b001/importcfg &lt;&lt; &#39;EOF&#39; # internal # import config packagefile fmt=/usr/lib/go/pkg/linux_amd64/fmt.a packagefile runtime=/usr/lib/go/pkg/linux_amd64/runtime.a EOF cd /home/vagrant/dive-into-go /usr/lib/go/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath &#34;$WORK/b001=&gt;&#34; -p main -complete -buildid RcHLBQbXBa2gQVsMR6P0/RcHLBQbXBa2gQVsMR6P0 -goversion go1.14.13 -D _/home/vagrant/dive-into-go -importcfg $WORK/b001/importcfg -pack ./empty_string.go ./string.go /usr/lib/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/_pkg_.a # internal cat &gt;$WORK/b001/importcfg.link &lt;&lt; &#39;EOF&#39; # internal packagefile _/home/vagrant/dive-into-go=$WORK/b001/_pkg_.">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#343a40">
<meta name="color-scheme" content="light dark"><meta property="og:title" content="Go语言内置分析工具" />
<meta property="og:description" content="Go 内置分析工具 # 这一章节将介绍Go 内置分析工具。通过这些工具我们可以分析、诊断、跟踪竞态GMP调度CPU耗用等问题。
go build # go build命令用来编译Go 程序。go build重要的命令行选项有以下几个
go build -n # -n选项用来显示编译过程中所有执行的命令不会真正执行。通过该选项我们可以查看编译器连接器如何工作的
# # _/home/vagrant/dive-into-go # mkdir -p $WORK/b001/ cat &gt;$WORK/b001/importcfg &lt;&lt; &#39;EOF&#39; # internal # import config packagefile fmt=/usr/lib/go/pkg/linux_amd64/fmt.a packagefile runtime=/usr/lib/go/pkg/linux_amd64/runtime.a EOF cd /home/vagrant/dive-into-go /usr/lib/go/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath &#34;$WORK/b001=&gt;&#34; -p main -complete -buildid RcHLBQbXBa2gQVsMR6P0/RcHLBQbXBa2gQVsMR6P0 -goversion go1.14.13 -D _/home/vagrant/dive-into-go -importcfg $WORK/b001/importcfg -pack ./empty_string.go ./string.go /usr/lib/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/_pkg_.a # internal cat &gt;$WORK/b001/importcfg.link &lt;&lt; &#39;EOF&#39; # internal packagefile _/home/vagrant/dive-into-go=$WORK/b001/_pkg_." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://go.cyub.vip/analysis-tools/go-buildin-tools/" /><meta property="article:section" content="analysis-tools" />
<title>Go语言内置分析工具 | 深入Go语言之旅</title>
<link rel="manifest" href="/manifest.json">
<link rel="icon" href="/favicon.png" >
<link rel="stylesheet" href="/book.min.f06572240ce28e67eb332ac5cf5d59a696c47ad4c6f700d5842c5ed93dd8ec77.css" integrity="sha256-8GVyJAzijmfrMyrFz11ZppbEetTG9wDVhCxe2T3Y7Hc=" crossorigin="anonymous">
<script defer src="/flexsearch.min.js"></script>
<script defer src="/en.search.min.17ed8785d618483565ce5458241250de0bb24d7b931b8b71446036ef43affd37.js" integrity="sha256-F&#43;2HhdYYSDVlzlRYJBJQ3guyTXuTG4txRGA270Ov/Tc=" crossorigin="anonymous"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-BQ229RRTTX"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-BQ229RRTTX', { 'anonymize_ip': false });
}
</script>
<!--
Made with Book Theme
https://github.com/alex-shpak/hugo-book
-->
</head>
<body dir="ltr">
<input type="checkbox" class="hidden toggle" id="menu-control" />
<input type="checkbox" class="hidden toggle" id="toc-control" />
<main class="container flex">
<aside class="book-menu">
<div class="book-menu-content">
<nav>
<h2 class="book-brand">
<a class="flex align-center" href="/"><img src="https://static.cyub.vip/images/202310/golang-480.png" alt="Logo" /><span>深入Go语言之旅</span>
</a>
</h2>
<div class="book-search">
<input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" />
<div class="book-search-spinner hidden"></div>
<ul id="book-search-results"></ul>
</div>
<ul>
<li>
<a href="https://www.cyub.vip/" target="_blank" rel="noopener">
个人博客
</a>
</li>
<li>
<a href="https://github.com/cyub" target="_blank" rel="noopener">
Github主页
</a>
</li>
<li>
<a href="https://www.topgoer.cn/?ref=go.cyub.vip" target="_blank" rel="noopener">
地鼠文档
</a>
</li>
</ul>
<ul>
<li>
<p><strong>
<a href="/">深入Go语言之旅</a></strong></p>
</li>
<li>
<p><strong>准备篇</strong></p>
<ul>
<li>
<a href="/compiler/">编译流程</a></li>
<li>
<a href="/analysis-tools/">分析工具</a>
<ul>
<li>
<a href="/analysis-tools/gdb/">GDB</a></li>
<li>
<a href="/analysis-tools/dlv/">Delve</a></li>
<li>
<a href="/analysis-tools/go-buildin-tools/"class=active>Go 内置工具</a></li>
</ul>
</li>
<li>
<a href="/go-assembly/">Go汇编</a></li>
</ul>
</li>
<li>
<p><strong>基础篇</strong></p>
<ul>
<li>
<a href="/type/">数据类型与数据结构</a>
<ul>
<li>
<a href="/type/string/">字符串</a></li>
<li>
<a href="/type/array/">数组</a></li>
<li>
<a href="/type/slice/">切片</a></li>
<li>
<a href="/type/nil/">nil</a></li>
<li>
<a href="/type/empty_struct/">空结构体</a></li>
<li>
<a href="/type/pointer/">指针</a></li>
<li>
<a href="/type/map/">映射</a></li>
</ul>
</li>
<li>
<a href="/function/">函数</a>
<ul>
<li>
<a href="/function/first-class/">一等公民</a></li>
<li>
<a href="/function/call-stack/">函数调用栈</a></li>
<li>
<a href="/function/pass-by-value/">值传递</a></li>
<li>
<a href="/function/closure/">闭包</a></li>
<li>
<a href="/function/method/">方法</a></li>
</ul>
</li>
<li>
<a href="/feature/">语言特性</a>
<ul>
<li>
<a href="/feature/comma-ok/">逗号ok模式</a></li>
<li>
<a href="/feature/for-range/">遍历 - for-range语法</a></li>
<li>
<a href="/feature/defer/">延迟执行 - defer语法</a></li>
<li>
<a href="/feature/select/">通道选择器 - select语法</a></li>
<li>
<a href="/feature/panic-recover/">恐慌与恢复 - panic/recover</a></li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>运行时篇</strong></p>
<ul>
<li>
<a href="/concurrency/">并发编程</a>
<ul>
<li>
<a href="/concurrency/memory-model/">内存模型</a></li>
<li>
<a href="/concurrency/context/">上下文 - context</a></li>
<li>
<a href="/concurrency/channel/">通道 - channel</a></li>
<li>
<a href="/concurrency/atomic/">原子操作 - atomic</a></li>
<li>
<a href="/concurrency/sync-map/">并发Map - sync.Map</a></li>
<li>
<a href="/concurrency/sync-waitgroup/">等待组 - sync.WaitGroup</a></li>
<li>
<a href="/concurrency/sync-once/">一次性操作 - sync.Once</a></li>
<li>
<a href="/concurrency/sync-pool/">缓冲池 - sync.Pool</a></li>
<li>
<a href="/concurrency/sync-cond/">条件变量 - sync.Cond</a></li>
<li>
<a href="/concurrency/sync-mutex/">互斥锁 - sync.Mutex</a></li>
<li>
<a href="/concurrency/sync-rwmutex/">读写锁 - sync.RWMutex</a></li>
</ul>
</li>
<li>
<a href="/gmp/">G-M-P调度机制</a>
<ul>
<li>
<a href="/gmp/gmp-model/">调度机制概述</a></li>
<li>
<a href="/gmp/scheduler/">调度器</a></li>
</ul>
</li>
<li>
<a href="/memory/">内存管理</a>
<ul>
<li>
<a href="/memory/allocator/">内存分配器</a></li>
<li>
<a href="/memory/gc/">GC</a></li>
</ul>
</li>
<li>
<a href="/type-system/">类型系统</a>
<ul>
<li>
<a href="/type-system/type/">类型系统</a></li>
<li>
<a href="/type-system/interface/">接口</a></li>
<li>
<a href="/type-system/reflect/">反射</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
<script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
</div>
</aside>
<div class="book-page">
<header class="book-header">
<div class="flex align-center justify-between">
<label for="menu-control">
<img src="/svg/menu.svg" class="book-icon" alt="Menu" />
</label>
<strong>Go语言内置分析工具</strong>
<label for="toc-control">
</label>
</div>
</header>
<article class="markdown"><h1 id="go-内置分析工具">
Go 内置分析工具
<a class="anchor" href="#go-%e5%86%85%e7%bd%ae%e5%88%86%e6%9e%90%e5%b7%a5%e5%85%b7">#</a>
</h1>
<p>这一章节将介绍Go 内置分析工具。通过这些工具我们可以分析、诊断、跟踪竞态GMP调度CPU耗用等问题。</p>
<h2 id="go-build">
go build
<a class="anchor" href="#go-build">#</a>
</h2>
<p><code>go build</code>命令用来编译Go 程序。<code>go build</code>重要的命令行选项有以下几个:</p>
<h3 id="go-build--n">
go build -n
<a class="anchor" href="#go-build--n">#</a>
</h3>
<p><code>-n</code>选项用来显示编译过程中所有执行的命令,不会真正执行。通过该选项我们可以查看编译器,连接器如何工作的:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># _/home/vagrant/dive-into-go</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mkdir -p $WORK/b001/
</span></span><span style="display:flex;"><span>cat &gt;$WORK/b001/importcfg <span style="color:#e6db74">&lt;&lt; &#39;EOF&#39; # internal
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"># import config
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile fmt=/usr/lib/go/pkg/linux_amd64/fmt.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile runtime=/usr/lib/go/pkg/linux_amd64/runtime.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>cd /home/vagrant/dive-into-go
</span></span><span style="display:flex;"><span>/usr/lib/go/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath <span style="color:#e6db74">&#34;</span>$WORK<span style="color:#e6db74">/b001=&gt;&#34;</span> -p main -complete -buildid RcHLBQbXBa2gQVsMR6P0/RcHLBQbXBa2gQVsMR6P0 -goversion go1.14.13 -D _/home/vagrant/dive-into-go -importcfg $WORK/b001/importcfg -pack ./empty_string.go ./string.go
</span></span><span style="display:flex;"><span>/usr/lib/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/_pkg_.a <span style="color:#75715e"># internal</span>
</span></span><span style="display:flex;"><span>cat &gt;$WORK/b001/importcfg.link <span style="color:#e6db74">&lt;&lt; &#39;EOF&#39; # internal
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile _/home/vagrant/dive-into-go=$WORK/b001/_pkg_.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile fmt=/usr/lib/go/pkg/linux_amd64/fmt.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile runtime=/usr/lib/go/pkg/linux_amd64/runtime.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile errors=/usr/lib/go/pkg/linux_amd64/errors.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/fmtsort=/usr/lib/go/pkg/linux_amd64/internal/fmtsort.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile io=/usr/lib/go/pkg/linux_amd64/io.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile math=/usr/lib/go/pkg/linux_amd64/math.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile os=/usr/lib/go/pkg/linux_amd64/os.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile reflect=/usr/lib/go/pkg/linux_amd64/reflect.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile strconv=/usr/lib/go/pkg/linux_amd64/strconv.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile sync=/usr/lib/go/pkg/linux_amd64/sync.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile unicode/utf8=/usr/lib/go/pkg/linux_amd64/unicode/utf8.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/bytealg=/usr/lib/go/pkg/linux_amd64/internal/bytealg.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/cpu=/usr/lib/go/pkg/linux_amd64/internal/cpu.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile runtime/internal/atomic=/usr/lib/go/pkg/linux_amd64/runtime/internal/atomic.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile runtime/internal/math=/usr/lib/go/pkg/linux_amd64/runtime/internal/math.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile runtime/internal/sys=/usr/lib/go/pkg/linux_amd64/runtime/internal/sys.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/reflectlite=/usr/lib/go/pkg/linux_amd64/internal/reflectlite.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile sort=/usr/lib/go/pkg/linux_amd64/sort.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile math/bits=/usr/lib/go/pkg/linux_amd64/math/bits.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/oserror=/usr/lib/go/pkg/linux_amd64/internal/oserror.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/poll=/usr/lib/go/pkg/linux_amd64/internal/poll.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/syscall/execenv=/usr/lib/go/pkg/linux_amd64/internal/syscall/execenv.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/syscall/unix=/usr/lib/go/pkg/linux_amd64/internal/syscall/unix.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/testlog=/usr/lib/go/pkg/linux_amd64/internal/testlog.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile sync/atomic=/usr/lib/go/pkg/linux_amd64/sync/atomic.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile syscall=/usr/lib/go/pkg/linux_amd64/syscall.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile time=/usr/lib/go/pkg/linux_amd64/time.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile unicode=/usr/lib/go/pkg/linux_amd64/unicode.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">packagefile internal/race=/usr/lib/go/pkg/linux_amd64/internal/race.a
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>mkdir -p $WORK/b001/exe/
</span></span><span style="display:flex;"><span>cd .
</span></span><span style="display:flex;"><span>/usr/lib/go/pkg/tool/linux_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode<span style="color:#f92672">=</span>exe -buildid<span style="color:#f92672">=</span>nR64Q3qx-0ZdNI4_-qJS/RcHLBQbXBa2gQVsMR6P0/RcHLBQbXBa2gQVsMR6P0/nR64Q3qx-0ZdNI4_-qJS -extld<span style="color:#f92672">=</span>gcc $WORK/b001/_pkg_.a
</span></span><span style="display:flex;"><span>/usr/lib/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/exe/a.out <span style="color:#75715e"># internal</span>
</span></span><span style="display:flex;"><span>mv $WORK/b001/exe/a.out dive-into-go
</span></span></code></pre></div><h3 id="go-build--race">
go build -race
<a class="anchor" href="#go-build--race">#</a>
</h3>
<p><code>-race</code>选项用来检查代码中是否存在竞态问题。<code>-race</code>可以用在多个子命令中:</p>
<pre tabindex="0"><code>go test -race mypkg
go run -race mysrc.go
go build -race mycmd
go install -race mypkg
</code></pre><p>下面是来自Go语言官方博客的一个示例<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>,在该示例中演示了使用<code>-race</code>选项检查代码中的竞态问题:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">start</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Timer</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">t</span> = <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">AfterFunc</span>(<span style="color:#a6e22e">randomDuration</span>(), <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Sub</span>(<span style="color:#a6e22e">start</span>))
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Reset</span>(<span style="color:#a6e22e">randomDuration</span>())
</span></span><span style="display:flex;"><span> })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">randomDuration</span>() <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>(<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Int63n</span>(<span style="color:#ae81ff">1e9</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>上面代码完成的功能是通过<code>time.AfterFunc</code>创建定时器,该定时器会在<code>randomDuration()</code>时候打印消息,此外还会通过<code>Rest()</code>方法重置该定时器,以达到重复利用该定时器目的。</p>
<p>当我们使用<code>-race</code>选项执行检查时候,可以发现上面代码是存在竞态问题的:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ go run -race main.go
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>WARNING: DATA RACE
</span></span><span style="display:flex;"><span>Read by goroutine 5:
</span></span><span style="display:flex;"><span> main.func·001<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span> race.go:14 +0x169
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Previous write by goroutine 1:
</span></span><span style="display:flex;"><span> main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span> race.go:15 +0x174
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">5</span> <span style="color:#f92672">(</span>running<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span> time.goFunc<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span> src/pkg/time/sleep.go:122 +0x56
</span></span><span style="display:flex;"><span> timerproc<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span> src/pkg/runtime/ztime_linux_amd64.c:181 +0x189
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span></code></pre></div><h3 id="go-build--gcflags">
go build -gcflags
<a class="anchor" href="#go-build--gcflags">#</a>
</h3>
<p><code>-gcflags</code>选项用来设置编译器编译时参数,支持的参数有:</p>
<ul>
<li>-N选项指示禁止优化</li>
<li>-l选项指示禁止内联</li>
<li>-S选项指示打印出汇编代码</li>
<li>-m选项指示打印出变量变量逃逸信息<code>-m -m</code>可以打印出更丰富的变量逃逸信息</li>
</ul>
<p><code>-gcflags</code>支持只在编译特定包时候才传递编译参数,此时的<code>-gcflags</code>格式为<code>包名=参数列表</code></p>
<pre tabindex="0"><code>go build -gcflags=&#34;-N -l -S&#34; main.go // 打印出main.go对应的汇编代码
go build -gcflags=&#34;log=-N -l&#34; main.go // 只对log包进行禁止优化禁止内联操作
</code></pre><h2 id="go-tool-compile">
go tool compile
<a class="anchor" href="#go-tool-compile">#</a>
</h2>
<p><code>go tool compile</code>命令用于汇编处理Go 程序文件。<code>go tool compile</code>支持常见选项有:</p>
<ul>
<li>-N选项指示禁止优化</li>
<li>-l选项指示禁止内联</li>
<li>-S选项指示打印出汇编代码</li>
<li>-m选项指示打印出变量内存逃逸信息</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>go tool compile -N -l -S main.go <span style="color:#75715e"># 打印出main.go对应的汇编代码</span>
</span></span><span style="display:flex;"><span>GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go tool compile -N -l -S main.go <span style="color:#75715e"># 打印出针对特定系统和CPU架构的汇编代码</span>
</span></span></code></pre></div><h2 id="go-tool-nm">
go tool nm
<a class="anchor" href="#go-tool-nm">#</a>
</h2>
<p><code>go tool nm</code>命令用来查看Go 二进制文件中符号表信息。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>go tool nm ./main | grep <span style="color:#e6db74">&#34;runtime.zerobase&#34;</span>
</span></span></code></pre></div><h2 id="go-tool-objdump">
go tool objdump
<a class="anchor" href="#go-tool-objdump">#</a>
</h2>
<p><code>go tool objdump</code>命令用来根据目标文件或二进制文件反编译出汇编代码。该命令支持两个选项:</p>
<ul>
<li>-S选项指示打印汇编代码</li>
<li>-s选项指示搜索相关的汇编代码</li>
</ul>
<pre tabindex="0"><code>go tool compile -N -l main.go # 生成main.o
go tool objdump main.o # 打印所有汇编代码
go tool objdump -s &#34;main.(main|add)&#34; ./test # objdump支持搜索特定字符串
</code></pre><h2 id="go-tool-trace">
go tool trace
<a class="anchor" href="#go-tool-trace">#</a>
</h2>
<h2 id="godebug环境变量">
GODEBUG环境变量
<a class="anchor" href="#godebug%e7%8e%af%e5%a2%83%e5%8f%98%e9%87%8f">#</a>
</h2>
<p><code>GODEBUG</code>是控制运行时调试的变量其参数以逗号分隔格式为name=val。<code>GODEBUG</code>可以用来观察GMP调度和GC过程。</p>
<h3 id="gmp调度">
GMP调度
<a class="anchor" href="#gmp%e8%b0%83%e5%ba%a6">#</a>
</h3>
<p>与GMP调度相关的两个参数</p>
<ul>
<li>
<p>schedtrace设置 schedtrace=X 参数可以使运行时在每 X 毫秒输出一行调度器的摘要信息到标准 err 输出中。</p>
</li>
<li>
<p>scheddetail设置 schedtrace=X 和 scheddetail=1 可以使运行时在每 X 毫秒输出一次详细的多行信息信息内容主要包括调度程序、处理器、OS 线程 和 Goroutine 的状态。</p>
</li>
</ul>
<p>我们以下面代码为例:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">2000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">a</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1e6</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">a</span> <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span> }()
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>执行以下代码获取GMP调度信息</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>GODEBUG<span style="color:#f92672">=</span>schedtrace<span style="color:#f92672">=</span><span style="color:#ae81ff">1000</span> go run ./test.go
</span></span></code></pre></div><p>笔者本人电脑输出以下内容:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>SCHED 0ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">6</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">4</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 0ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 0ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 0ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 1007ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">16</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 1000ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>SCHED 2018ms: gomaxprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> idleprocs<span style="color:#f92672">=</span><span style="color:#ae81ff">8</span> threads<span style="color:#f92672">=</span><span style="color:#ae81ff">16</span> spinningthreads<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> idlethreads<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span> runqueue<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> <span style="color:#f92672">[</span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">0</span> 0<span style="color:#f92672">]</span>
</span></span></code></pre></div><p>上面输出内容解释说明:</p>
<ul>
<li>SCHED XXms: SCHED是调度日志输出标志符。XXms是自程序启动之后到输出当前行时间</li>
<li>gomaxprocs P的数量等于当前的 CPU 核心数或者GOMAXPROCS环境变量的值</li>
<li>idleprocs 空闲P的数量与gomaxprocs的差值即运行中P的数量</li>
<li>threads 线程数量即M的数量</li>
<li>spinningthreads自旋状态线程的数量。当M没有找到可供其调度执行的 Goroutine 时,该线程并不会销毁,而是出于自旋状态</li>
<li>idlethreads空闲线程的数量</li>
<li>runqueue全局队列中G的数量</li>
<li>[0 0 0 0 0 0 0 0]表示P本地队列下G的数量有几个P中括号里面就会有几个数字</li>
</ul>
<h3 id="gc">
GC
<a class="anchor" href="#gc">#</a>
</h3>
<p>与 GC 相关的参数是 gctrace当设置为1时候会输出GC相关信息到标准错误输出。使用方式示例如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>GODEBUG<span style="color:#f92672">=</span>gctrace<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> go run main.go
</span></span></code></pre></div><p>GC 时候输出的内容格式如下:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span>gc # <span style="color:#f92672">@</span>#s #<span style="color:#f92672">%</span>: #<span style="color:#f92672">+</span>#<span style="color:#f92672">+</span># ms clock, #<span style="color:#f92672">+</span>#<span style="color:#f92672">/</span>#<span style="color:#f92672">/</span>#<span style="color:#f92672">+</span># ms cpu, #-&gt;#-&gt;# <span style="color:#66d9ef">MB</span>, # <span style="color:#66d9ef">MB</span> goal, # P
</span></span></code></pre></div><p>格式解释说明如下:</p>
<ul>
<li><strong>gc #</strong>GC 编号,每次 GC 时递增</li>
<li><strong>@#s</strong>:程序自启动以来的时间(单位秒)</li>
<li><strong>#%</strong>:程序自启动以来花费在 GC 上的时间百分比</li>
<li><strong>#+&hellip;+#</strong>GC 各阶段花费的时间分别为单个P的墙上时间和累计CPU时间</li>
<li><strong>#-&gt;#-&gt;# MB</strong>:分别表示 GC 启动时, GC 结束时, GC 活动时的堆大小</li>
<li><strong>#MB goal</strong>:下一次触发 GC 的内存占用阈值</li>
<li><strong>#P</strong>当前使用的处理器P的数量</li>
</ul>
<p>比如对于下面的输出内容,详细解释如下:</p>
<blockquote>
<p>gc 100 @0.904s 11%: 0.043+2.8+0.029 ms clock, 0.34+3.4/5.4/13.6+0.23 ms cpu, 10-&gt;11-&gt;6 MB, 12 MB goal, 8 P</p>
</blockquote>
<ul>
<li><strong>gc 100</strong>:第 100 次 GC</li>
<li><strong>@0.904s</strong>当前时间是程序启动后的0.904s</li>
<li><strong>11%</strong>:程序启动后到现在共花费 11% 的时间在 GC 上</li>
<li><strong>0.043+2.8+0.029 ms clock</strong>
<ul>
<li>0.043:表示单个 P 在 mark 阶段的 STW 时间</li>
<li>2.8:表示所有 P 的 concurrent mark并发标记所使用的时间</li>
<li>0.029:表示单个 P 的 markTermination 阶段的 STW 时间</li>
</ul>
</li>
<li><strong>0.34+3.4/5.4/0+0.23 ms cpu</strong>
<ul>
<li>0.34:表示整个进程在 mark 阶段 STW 停顿的时间一共0.34秒,即 0.043 * 8</li>
<li>3.4/5.4/13.63.4 表示 mutator assist 占用的时间5.4 表示 dedicated + fractional 占用的时间13.6 表示 idle 占用的时间。这三块累计时间为22.4即2.8 * 8</li>
<li>0.23 ms0.23 表示整个进程在 markTermination 阶段 STW 时间即0.029 * 8</li>
</ul>
</li>
<li><strong>10-&gt;11-&gt;6 MB</strong>
<ul>
<li>10表示开始 mark 阶段前的 heap_live 大小</li>
<li>11表示开始 markTermination 阶段前的 heap_live 大小</li>
<li>6表示被标记对象的大小</li>
</ul>
</li>
<li><strong>12 MB goal</strong>:表示下一次触发 GC 回收的阈值是 12 MB</li>
<li><strong>8 P</strong>:本次 GC 一共涉及8 个P</li>
</ul>
<h4 id="gogc参数">
GOGC参数
<a class="anchor" href="#gogc%e5%8f%82%e6%95%b0">#</a>
</h4>
<p>Go语言GC相关的另外一个参数是GOGC。GOGC 用于控制GC的处发频率 其值默认为100, 这意味着直到自上次垃圾回收后heap size已经增长了100%时GC才触发运行live heap size每增长一倍GC触发运行一次。若设定GOGC=200, 则live heap size 自上次垃圾回收后增长2倍时GC触发运行 总之其值越大则GC触发运行频率越低 反之则越高。如果GOGC=off 则关闭GC。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 表示当前应用占用的内存是上次GC时占用内存的两倍时触发GC</span>
</span></span><span style="display:flex;"><span>export GOGC<span style="color:#f92672">=</span><span style="color:#ae81ff">100</span>
</span></span></code></pre></div><h2 id="进一步阅读">
进一步阅读
<a class="anchor" href="#%e8%bf%9b%e4%b8%80%e6%ad%a5%e9%98%85%e8%af%bb">#</a>
</h2>
<!-- raw HTML omitted -->
<ul>
<li>
<a href="https://eddycjy.gitbook.io/golang/di-9-ke-gong-ju/go-tool-trace">Go 大杀器之跟踪剖析 trace</a></li>
<li>
<a href="https://pkg.go.dev/runtime#hdr-Environment_Variables">go runtime Environment Variables</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>
<a href="https://blog.golang.org/race-detector">Introducing the Go Race Detector</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
</article>
<footer class="book-footer">
<div class="flex flex-wrap justify-between">
</div>
<script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script>
</footer>
<div class="book-comments">
<script src="https://giscus.app/client.js" data-repo="cyub/go-1.14.13"
data-repo-id="MDEwOlJlcG9zaXRvcnkzMzc2ODUyMzQ=" data-category="Announcements"
data-category-id="DIC_kwDOFCCq8s4CZ3BC"
data-mapping="pathname" data-strict="0" data-emit-metadata="0"
data-input-position="bottom" data-reactions-enabled="0"
data-lang="zh-CN" data-theme="preferred_color_scheme" crossorigin="anonymous" async>
</script>
<noscript>Please enable JavaScript to view the comments powered by giscus.</noscript>
</div>
<label for="menu-control" class="hidden book-menu-overlay"></label>
</div>
</main>
</body>
</html>