go 打开文件句柄_Go中trace包的使用

Go给我们提供了一个工具trace,可以在运行时启用trace, 并获得程序执行情况的详细视图。

应该怎么使用trace呢?一般有下面三种使用方式

  • 运行go test的时候,带上-trace参数标记, go test -trace=trace.out

  • pprof中获取实时traceimport _ "net/http/pprof"

  • 在代码中编码嵌入trace,

代码中使用, 我写了一个简单的例子:

package main

import (
"io/ioutil"
"log"
"os"
"runtime/trace"
"sync"
)

// 一个简单的例子去演示如何在代码中使用trace包
func main() {
// 1. 创建trace持久化的文件句柄
f, err := os.Create("trace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()

// 2. trace绑定文件句柄
if err := trace.Start(f); err != nil {
log.Fatalf("failed to start trace: %v", err)
}
defer trace.Stop()

// 下面就是你的监控的程序
// 我简单写了一个文件读写
var wg sync.WaitGroup
wg.Add(2)

// 一个协程用来读文件
go func() {
defer wg.Done()
ioutil.ReadFile(`mxc.txt`)
}()

// 写文件协程
go func() {
defer wg.Done()
ioutil.WriteFile(`mxc_code.txt`, []byte(`码小菜`), 0644)
}()

wg.Wait()
}

运行命令

ce90e027717e8ee60742228e51ae8540.png

会看到生成一个trace.out的文件,打开命令

dd3ad447574d3739827abd65f6d049ba.png

这时候会打开一个浏览器窗口,可以看到

26af8e9ea35f6f34c5bcbf305b40c383.png

工作原理

trace的工作流程非常简单, 标准库会记录Go运行期间的每个事件,例如内存分配, 垃圾收集器(gc)的所有阶段, goroutine何时运行,暂停等等,并将这些信息格式化以供以后显示使用。

  • 在开始记录之前,runtimeSTW,并对当前goroutines的状态进行快照。

0f1ff9fdc7bef2f477f852f4c7deec87.png

  • stw后,收集的事件会放入trace的一个缓冲区,在达到缓冲区最大容量时刷新。下面是整个流程图

 ef9c4ec2d644dd598b3427b61448b5d2.png

  • tracer现在就可以将上面收集的信息dump到输出。所以,当trace开始时,Go会专门为trace启动一个goroutine。这个goroutine会在数据可用时dump记录的数据到输出,在没有数据时park住。如下图:ab9f3ab2616463ce35099d0ad0afe0b0.png

现在的流程就非常清晰了

一旦生成了跟踪信息,就可以用命令go tool trace output.out来完成可视化, 下图是我刚开始那个简单程序的一个trace图,点击图一的View Trace

14bd0f07e55e8b57344de61786c6b416.png

上面的代码相对简单,看不到更多的关于gc的信息,下面我写了一个触发gc的程序

package main

import (
"log"
"os"
"runtime/trace"
"sync"
)

func main() {
// 1. 创建trace持久化的文件句柄
f, err := os.Create("trace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()

// 2. trace绑定文件句柄
if err := trace.Start(f); err != nil {
log.Fatalf("failed to start trace: %v", err)
}
defer trace.Stop()

// 下面就是你的监控的程序
var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
_ = make([]int, 0, 20000)
}
}()

go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
_ = make([]int, 0, 10000)
}
}()

wg.Wait()
}

图示很直接。与gc相关的信息,如下图0e02d13459f7f1966d107f7b7fdf65e6.png

下面是一个大致的描述

  • STW 是gc中的两个"停止世界"的阶段。在这两个阶段中,goroutine会停止运行。

d6482b7cd126485f76d16069e2ba1e5e.png

  • GC(idle) 指没有标记内存时的goroutine。

3bbbaf5b622c9d8fdf64c482dbb886cc.png

  • MARK ASSIST 在分配内存过程中重新标记内存(mark the memory)的goroutine。

b4fbee02fa84a5ae5f325946480fa3ec.png

  • SWEEP 垃圾清理

fa310773c5e2694f6ddffde3e5dba635.png

  • GXX runtime.gcBgMarkWorker 是帮助标记内存的专用后台goroutine。

b0655f806449276c03c291fdbbdba378.png

然而,有些Trace并不容易理解:

  • proc start当一个逻辑处理器§与一个线程绑定时被调用。也就是启动新线程或从系统调用恢复

4f88745d55d4702d43735a8a19a80428.png

  • proc stop当线程与当前处理器§分离时,将调用proc stop。当线程发生系统调用被阻塞或线程退出时,就会发生这种情况。

c98993f9b7433c44da42f5654519bb91.png

  • goroutine进行系统调用时,将调用syscall

ec54c7f909d9c9f1ff97b2352746ce73.png

注意:Trace既可以定义和可视化自己的Trace以及也可以追踪标准库中的Trace

用户Traces

用户级别可以定义的Trace具有两个层次级别:

  • 在任务的最高层,有开始和结束

  • 在该区域的子级别上

这里有一个简单的例子:

package main

import (
"context"
"io/ioutil"
"log"
"os"
"runtime/trace"
"sync"
)

// 一个简单的例子去演示如何在代码中使用trace包
func main() {
// 1. 创建trace持久化的文件句柄
f, err := os.Create("trace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()

// 2. trace绑定文件句柄
if err := trace.Start(f); err != nil {
log.Fatalf("failed to start trace: %v", err)
}
defer trace.Stop()

ctx, task := trace.NewTask(context.Background(), "main start")
var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
r := trace.StartRegion(ctx, "reading file")
defer r.End()

ioutil.ReadFile(`n1.txt`)
}()

go func() {
defer wg.Done()
r := trace.StartRegion(ctx, "writing file")
defer r.End()

ioutil.WriteFile(`n2.txt`, []byte(`42`), 0644)
}()

wg.Wait()
defer task.End()
}

可以通过菜单中的用户自定义任务直接从工具中可视化这些Trace:

950e4a527173868a1e3e6ff165549663.png

也可以记录一些其他的信息到任务:

ctx, task := trace.NewTask(context.Background(), "main start")
trace.Log(ctx, "category", "I/O file")
trace.Log(ctx, "goroutine", "2")

这些日志将在设置任务的goroutine下找到:

18e511436d13ef6681f9eba643eb6066.png

一个简单的性能测试,可以帮助理解Trace对应用程序的影响。一个将带有-trace标志,而另一个则不带-trace。以下是带有ioutil.ReadFile()函数的性能测试的结果,该函数会生成大量事件:

name         time/op
ReadFiles-8 48.1µs ± 0%
name time/op
ReadFiles-8 63.5µs ± 0% // with tracing

在这种情况下,性能影响约为35%,并且可能和应用有关系, 你可以根据你的应用性能来决定要用不用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值