有创意的婚纱网站模板下载,专业建设网站服务公司,wordpress快速网店主题,网络营销方案撰写的内容与要求注#xff1a; 转载请注明出处#xff0c; 原文链接。
概述
在这篇博客中#xff0c;我将详细介绍 xgo 的实现细节。
如果你不知道#xff0c;xgo 项目位于 https://github.com/xhd2015/xgo。
它的作用很简单#xff0c;就是在每个 Go 函数的开头添加拦截器#xff0…注 转载请注明出处 原文链接。
概述
在这篇博客中我将详细介绍 xgo 的实现细节。
如果你不知道xgo 项目位于 https://github.com/xhd2015/xgo。
它的作用很简单就是在每个 Go 函数的开头添加拦截器从而引入了所谓的 Trap 概念然后在此基础上引入了其他功能比如 Mock、Patch 和 Trace。
什么是 Trap
Trap 是插入到函数体开头的一段代码。以一个名为 greet 的函数为例
func greet(s string) string {return hello s
}经过xgo的处理后编译器看到的代码将变为
import runtimefunc greet(s string) (r0 string){stop, post : runtime.__xgo_trap(greet, s, r0)if stop {return}defer post()return hello s
}这两者的差异可以通过下面的图表来可视化
如图所示一旦函数被调用它的控制流首先转移到 Trap然后一系列拦截器将根据其目的检查当前调用是否应该被Mock、修改、记录或停止。
这个想法很简单但也引发了一些问题
编译器如何看到插桩后的代码import runtime 是什么
这两个问题反映了 xgo 的两个基本部分编译器插桩和运行时插桩。
让我们先看看第一个问题。
编译器如何看到插桩后的代码
为了让编译器看到与其原始源代码不同的代码中间必须发生某种事情。
有趣的是go build 有一个名为-toolexec的标志
$ go help build
...
-toolexec cmd argsa program to use to invoke toolchain programs like vet and asm.For example, instead of running asm, the go command will runcmd args /path/to/asm arguments for asm.The TOOLEXEC_IMPORTPATH environment variable will be set,matching go list -f {{.ImportPath}} for the package being built.
...如果你搜索 go toolexec甚至有一个示例https://go.dev/src/cmd/go/testdata/script/toolexec.txt。
简而言之-toolexec 标志允许用户拦截 go 调用的每个 compile 和 link 命令并根据需要执行某种插桩如下图所示 请注意当你在 go build 中添加 -toolexecmy_tool 标志时它不会直接调用 compile args 和 link args而是将这些调用转发给 my_tool cmd args。
因此xgo 利用这个标志来拦截 compile 命令将所有的编译转发到增强后的编译器。
然后增强后的编译器将在每个函数中插入这些Trap调用为运行时在实际调用之前捕获函数调用提供机会。
import runtime 是什么
现在编译器已经为我们添加了Trap调用我们如何知道需要进行什么样的检查
我们不能让每个包都依赖于 xgo因为它们可能并不需要它。
好在 runtime 也被插桩了将调用转发给 xgo。因为在 Go 中每个包都隐式依赖于 runtime 包。控制流程如下图所示
这实际上是一种依赖注入。这样一来用户的代码就不必显式依赖xgo。
上述代码可以在 runtime/trap_runtime/xgo_trap.go 和 runtime/trap/trap.go 中找到。
为了使Trap可扩展xgo 引入了一个名为 interceptor 的概念。它具有以下签名
type Interceptor struct {Pre func(ctx context.Context, f *core.FuncInfo, args core.Object, result core.Object) (data interface{}, err error)Post func(ctx context.Context, f *core.FuncInfo, args core.Object, result core.Object, data interface{}) error
}一个 interceptor 由两个子函数组成称为 Pre 和 Post。
Pre 在函数逻辑之前调用Post 在函数逻辑之后使用 defer 语句调用。
总结
让我们总结一下我们所讨论的内容。
当你运行 xgo test ./ 时它会执行以下操作
找到 GOROOT将 GOROOT 复制到 ~/.xgo/go-instruments/GOROOT 以准备进行插桩对 ~/.xgo/go-instruments/GOROOT 进行补丁包括编译器和运行时构建插桩的编译器使用额外的标志调用 go buildgo build -toolexecexec_tool ./exec_tool 然后将所有编译命令转发给插桩的编译器一旦所有编译完成go 调用 link 生成可执行文件你就得到了一个插桩的二进制文件
优点和缺点
因此xgo 从上述机制中获得了优点和缺点。 优点
并发安全它不会替换需要修改全局地址的函数因此每个 goroutine 可以设置自己的拦截器并单独删除它们兼容性它重写源代码而不是架构指令因此与操作系统和架构无关可扩展性它提供了通用的拦截器因此它的用途不仅限于模拟你可以借鉴 GRPC 拦截器的所有用途比如已经实现的追踪、缓存、日志记录等…
缺点
用户需要安装 xgo 才能启用陷阱功能。
感谢阅读xgo的核心实现已经在上面全部介绍了。你对此有什么看法请在这里留下评论让我们一起讨论吧