xgo: golang基于-toolexec实现猴子补丁

注: 转载请注明出处, 原文链接。

概述

在这篇博客中,我将详细介绍 xgo 的实现细节。

如果你不知道,xgo 项目位于 https://github.com/xhd2015/xgo。

它的作用很简单,就是在每个 Go 函数的开头添加拦截器,从而引入了所谓的 Trap 概念,然后在此基础上引入了其他功能,比如 MockPatchTrace

什么是 Trap?

Trap 是插入到函数体开头的一段代码。以一个名为 greet 的函数为例:

func greet(s string) string {return "hello " + s
}

经过xgo的处理后,编译器看到的代码将变为:

import "runtime"func 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 args'a program to use to invoke toolchain programs like vet and asm.For example, instead of running asm, the go command will run'cmd 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 调用的每个 compilelink 命令,并根据需要执行某种插桩,如下图所示:
在这里插入图片描述
请注意,当你在 go build 中添加 -toolexec=my_tool 标志时,它不会直接调用 compile argslink 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 由两个子函数组成,称为 PrePost

  • Pre 在函数逻辑之前调用,
  • Post 在函数逻辑之后使用 defer 语句调用。

总结

让我们总结一下我们所讨论的内容。

当你运行 xgo test ./ 时,它会执行以下操作:

  1. 找到 GOROOT,
  2. 将 GOROOT 复制到 ~/.xgo/go-instruments/GOROOT 以准备进行插桩,
  3. 对 ~/.xgo/go-instruments/GOROOT 进行补丁,包括编译器和运行时,
  4. 构建插桩的编译器,
  5. 使用额外的标志调用 go build:go build -toolexec=exec_tool ./
  6. exec_tool 然后将所有编译命令转发给插桩的编译器,
  7. 一旦所有编译完成,go 调用 link 生成可执行文件,你就得到了一个插桩的二进制文件!

优点和缺点

因此,xgo 从上述机制中获得了优点和缺点。
优点:

  • 并发安全:它不会替换需要修改全局地址的函数,因此每个 goroutine 可以设置自己的拦截器并单独删除它们,
  • 兼容性:它重写源代码而不是架构指令,因此与操作系统和架构无关,
  • 可扩展性:它提供了通用的拦截器,因此它的用途不仅限于模拟,你可以借鉴 GRPC 拦截器的所有用途,比如已经实现的追踪、缓存、日志记录等…

缺点:

  • 用户需要安装 xgo 才能启用陷阱功能。

感谢阅读,xgo的核心实现已经在上面全部介绍了。你对此有什么看法?请在这里留下评论,让我们一起讨论吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/600728.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全志R128 SDK HAL 模块开发指南——IR红外无线传输

IR 红外是一种电磁波&#xff0c;可以实现数据的无线传输&#xff0c;由发送和接收两个部分组成。发送端对红外信号进行脉冲编码&#xff0c;接收端完成对红外信号的脉冲解码。红外遥控协议有多种&#xff0c;如 NEC、SIRC、 RC-5 等&#xff0c;这些协议都比较简单&#xff0…

【触想智能】工业一体机和普通电脑的区别是什么?

工业一体机和普通电脑的区别是什么&#xff0c;工业一体机可以当普通电脑一样使用吗? 要想了解工业一体机和普通电脑的区别是什么?我们首先来看看工业一体机是什么&#xff0c;它跟普通电脑有哪些相似的地方?下面小编就为大家来详细介绍一下。 在工作原理上&#xff0c;工业…

镭速集成SMTP开发邮件发送客户端

随着信息技术的不断进步&#xff0c;电子邮件已经成为商务交流和日常信息传递的关键工具。开发邮件客户端&#xff0c;尤其是整合了SMTP服务的客户端&#xff0c;对于增强企业内部的沟通效率和提升客户服务品质扮演着至关重要的角色。本文将深入探讨如何通过整合SMTP服务&#…

Linux系统Docker部署Apache Superset并实现公网实时访问本地数据

文章目录 前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网穿透&#xff0c;实现公网访问3. 设置固定连接公网地址 前言 Superset是一款由中国知名科技公司开源的“现代化的…

基于springboot实现母婴商城管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现母婴商城系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本母婴商城系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在…

Vue - 你知道Vue2中对象动态新增属性,视图无法更新的原因吗

难度级别:中高级及以上 提问概率:55% 这道题面试官会这样描述,比如有这样一个场景,一个对象里有name属性,可以正常显示在页面中。但后续动态添加了一个age属性,通过调试打印发现对象里的age属性已经添加了上了,但试图中却没有展示出来,…

pytest的时候输出一个F后面跟很多绿色的点解读

使用pytest来测试pyramid和kotti项目&#xff0c;在kotti项目测试的时候&#xff0c;输出一个F后面跟很多绿色的点&#xff0c;是什么意思呢&#xff1f; 原来在使用pytest进行测试时&#xff0c;输出中的“F”代表一个失败的测试&#xff08;Failed&#xff09;&#xff0c;而…

C#+net微信预约挂号系统源码,掌上医院挂号,在线缴费、检查、检验报告,住院服务

微信公众号预约挂号系统、支付宝小程序预约挂号系统主要是让自费、医保患者在手机上就能实现就医全过程&#xff0c;实时预约挂号、自费、医保结算&#xff0c;同时还可以查询检查检验报告等就诊信息&#xff0c;真正实现了让信息“多跑路”&#xff0c;让群众“少跑腿”。系统…

MySQL 底层数据结构 聚簇索引以及二级索引 Explain的使用

数据结构 我们知道MySQL的存储引擎Innodb默认底层是使用B树的变种来存储数据的 下面我们来复习一下B树存储 B树存储 哈希存储的区别 哈希存储,只能使用等值查询 B树与B树存储 我们知道B树实际上就是B树的变种 那么为啥使用B树而不是使用B树呢? 我们知道效率的高低主要取决于…

JUC基础

1.JUC概念 JUC是文件Java官方文档下面的java.Util下面的工具包。作用于多线程&#xff0c;内容有lock锁&#xff0c;以及callable等内容。JDK官方文档路径。基础多线程不了解可以看多线程子线程结束&#xff0c;执行主线程 2.线程、进程 1.进程&#xff1a; 一个程序是线程…

Python第四次作业

周六&#xff1a; 1. 找出10000以内能被5或6整除&#xff0c;但不能被两者同时整除的数&#xff08;函数&#xff09; def find_number():for number in range(0,10000):if number % 5 0 or number % 6 0:if number % 5 ! number % 6:ls.append(number)print(ls)ls [] fin…

如何快速摸清一个行业?

作为一名职场人&#xff0c;或多或少都会遇到需要了解自己不熟悉的行业&#xff0c;比如&#xff1a; 职业选择&#xff0c;跳槽换工作时&#xff1a;哪家企业所在的行业有优势&#xff0c;未来会有更多的机会&#xff1f;哪个行业给的薪资会更高&#xff1f;行业内当下及未来的…