当前位置: 首页 > news >正文

网站界面要素房地产信息管理系统软件

网站界面要素,房地产信息管理系统软件,网上做翻译兼职网站,旅游网站开发技术文档一、前言 不久前#xff0c;阿里云 ARMS 团队、编译器团队、MSE 团队携手合作#xff0c;共同发布并开源了 Go 语言的编译时自动插桩技术。该技术以其零侵入的特性#xff0c;为 Go 应用提供了与 Java 监控能力相媲美的解决方案。开发者只需将 go build 替换为新编译命令 o… 一、前言 不久前阿里云 ARMS 团队、编译器团队、MSE 团队携手合作共同发布并开源了 Go 语言的编译时自动插桩技术。该技术以其零侵入的特性为 Go 应用提供了与 Java 监控能力相媲美的解决方案。开发者只需将 go build 替换为新编译命令 otel go build就能实现对 Go 应用的全面监控和治理。 二、问题描述 近期我们收到用户反馈使用 otel go build -race 替代正常的 go build -race 命令后编译生成的程序会导致崩溃。-race[3]是 Go 编译器的一个参数用于检测数据竞争data race问题。通过为每个变量的访问添加额外检查确保多个 goroutine 不会以不安全方式同时访问这些变量。 理论上我们的工具不应影响-race 竞态检查的代码因此出现崩溃的现象是非预期的所以我们花了一些时间排查这个崩溃问题崩溃的堆栈信息如下 (gdb) bt#0 0x000000000041e1c0 in __tsan_func_enter ()#1 0x00000000004ad05a in racecall ()#2 0x0000000000000001 in ?? ()#3 0x00000000004acf99 in racefuncenter ()#4 0x00000000004ae7f1 in runtime.racefuncenter (callpc4317632)#5 0x0000000000a247d8 in ../sdk/trace.(*traceContext).TakeSnapShot (tcoptimized out, ~r0...)#6 0x00000000004a2c25 in runtime.contextPropagate#7 0x0000000000480185 in runtime.newproc1.func1 () #8 0x00000000004800e2 in runtime.newproc1 (fn0xc00030a1f0, callergp0xc0000061e0, callerpc12379404, retVal00xc0002c8f00)#9 0x000000000047fc3f in runtime.newproc.func1 () #10 0x00000000004a992a in runtime.systemstack ()....可以看到崩溃源于 __tsan_func_enter而引发该问题的关键点是 runtime.contextPropagate。我们的工具在 runtime.newproc1 函数的开头插入了以下代码 func newproc1(fn *funcval, callergp *g, callerpc uintptr) (retVal0 *g) { // 我们插入的代码 retVal0.otel_trace_context contextPropagate(callergp.otel_trace_context)...} // 我们插入的代码func contextPropagate(tls interface{}) interface{} { if tls nil { return nil } if taker, ok : tls.(ContextSnapshoter); ok { return taker.TakeSnapShot() } return tls} // 我们插入的代码func (tc *traceContext) TakeSnapShot() interface{} { ...}TakeSnapShot 被 Go 编译器在函数入口和出口分别注入了 racefuncenter() 和 racefuncexit()最终调用 __tsan_func_enter 导致崩溃。由此确定崩溃问题确实是我们的注入代码导致的继续深入排查。 三、排查过程 3.1 崩溃根源 使用 objdump 查看 __tsan_func_enter 的源码看到它接收两个函数参数出错的地方是第一行 mov 0x10(%rdi),%rdx它约等于 rdx *(rdi 0x10)。打印寄存器后发现 rdi 0根据调用约定rdi 存放的是第一个函数参数因此这里的问题就是函数第一个参数 thr 为 0。 // void __tsan_func_enter(ThreadState *thr, void *pc);000000000041e1c0 __tsan_func_enter: 41e1c0: 48 8b 57 10 mov 0x10(%rdi),%rdx 41e1c4: 48 8d 42 08 lea 0x8(%rdx),%rax 41e1c8: a9 f0 0f 00 00 test $0xff0,%eax ...那么第一个参数 thr 是谁传进来的呢接着往上分析调用链。 3.2 调用链分析 出错的整个调用链是 racefuncenter(Go) - racecall(Go) - __tsan_func_enter(C)。需要注意的是前两个函数都是 Go 代码Go 函数调用 Go 函数遵循 Go 的调用约定。在 amd64 平台前九个函数参数使用以下寄存器 另外以下寄存器用于特殊用途 后两个函数一个 Go 代码一个 C 代码Go 调用 C 的情况下遵循 System V AMD64 调用约定在 Linux 平台上使用以下寄存器作为前六个参数 理解了 Go 和 C 的调用约定之后再来看整个调用链的代码 TEXT racefuncenter(SB), NOSPLIT|NOFRAME, $0-0 MOVQ DX, BXx MOVQ g_racectx(R14), RARG0 // RSI存放thr MOVQ R11, RARG1 // RDI存放pc MOVQ $__tsan_func_enter(SB), AX // AX存放__tsan_func_enter函数指针 CALL racecall(SB) MOVQ BX, DX RETTEXT racecall(SB), NOSPLIT|NOFRAME, $0-0 ... CALL AX // 调用__tsan_func_enter函数指针 ...racefuncenter 将 g_racectx(R14) 和 R11 分别放入 C 调用约定的参数寄存器 RSI(RARG0) 和 RDI(RARG1)并将 __tsan_func_enter 放入 Go 调用约定的参数寄存器 RAX然后调用 racecall它进一步调用 __tsan_func_enter(RAX)这一系列操作大致相当于 __tsan_func_enter(g_racectx(R14), R11)。 不难看出问题的根源在于 g_racectx(R14) 为 0。根据 Go 的调用约定 R14 存放当前 goroutine 它不可能为 0 因此出问题的必然是 R14.racectx 字段为 0。为了避免无效努力通过调试器 dlv 二次确认 (dlv) p *(*runtime.g)(R14)runtime.g { racectx: 0, ...}那么为什么当前 R14.racectx 为 0下一步看看 R14 具体的状态。 3.3 协调程度 func newproc(fn *funcval) { gp : getg() pc : sys.GetCallerPC() #1 systemstack(func() { newg : newproc1(fn, gp, pc, false, waitReasonZero) #2 ... })}经过排查在代码 #1 处R14.racectx 是正常的但到了代码 #2 处R14.racectx 就为空了原因是 systemstack 被调用它有一个切换协程的动作具体如下 // func systemstack(fn func())TEXT runtime·systemstack(SB), NOSPLIT, $0-8 ... // 切换到g0协程 MOVQ DX, g(CX) MOVQ DX, R14 // 设置 R14 寄存器 MOVQ (g_schedgobuf_sp)(DX), SP// 在g0协程上运行目标函数fn MOVQ DI, DX MOVQ 0(DI), DI CALL DI// 切换回原始协程 ...原来 systemstack 有一个切换协程的动作会先把当前协程切换成 g0然后执行 fn最后恢复原始协程执行。 在 Go 语言的 GMPGoroutine-Machine-Processor调度模型中每个系统级线程 M 都拥有一个特殊的 g0 协程以及若干用于执行用户任务的普通协程 g。g0 协程主要负责当前 M 上用户 g 的调度工作。由于协程调度是不可抢占的调度过程中会临时切换到系统栈system stack上执行代码。在系统栈上运行的代码是隐式不可抢占的并且垃圾回收器不会扫描系统栈。 到这里我们已经知道执行 newproc1 时的协程总是 g0而 g0.racectx 是在 main 执行开始时被主动设置为 0最终导致程序崩溃 // src/runtime/proc.go#main// The main goroutine.func main() { mp : getg().m// g0 的 racectx 仅用于作为主 goroutine 的父级。 // 不应将其用作其他目的。 mp.g0.racectx 0 ...四、解决方案 到这里基本上可以做一个总结了程序崩溃的原因如下 newproc1 中插入的 contextPropagate 调用 TakeSnapshot而 TakeSnapshot 被 go build -race 强行在函数开始插入了 racefuncenter() 函数调用该函数将使用 racectx。 newproc1 是在 g0 协程执行下运行该协程的 racectx 字段是 0最终导致崩溃。 一个解决办法是给 TakeSnapshot 加上 Go 编译器的特殊指令 //go:norace该指令需紧跟在函数声明后面用于指定该函数的内存访问将被竞态检测器忽略Go 编译器将不会强行插入 racefuncenter()调用。 五、疑惑一 runtime.newproc1 中不只调用了我们注入的 contextPropagate还有其他函数调用为什么这些函数没有被编译器插入 race 检查的代码如 racefuncenter 经过排查后发现Go 编译器会特殊处理 runtime 包针对 runtime 包中的代码设置 NoInstrument 标志从而跳过生成 race 检查的代码 // /src/cmd/internal/objabi/pkgspecial.govar pkgSpecialsOnce sync.OnceValue(func() map[string]PkgSpecial { ... for _, pkg : range runtimePkgs { set(pkg, func(ps *PkgSpecial) { ps.Runtime true ps.NoInstrument true }) } ...})六、疑惑二 理论上插入 //go:norace 之后问题应该得到解决但实际上程序还是发生了崩溃。经过排查发现TakeSnapShot 中有 map 初始化和 map 循环操作这些操作会被编译器展开成 mapinititer() 等函数调用。这些函数直接手动启用了竞态检测器而且无法加上 //go:norace func mapiterinit(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { if raceenabled m ! nil { // 主动的race检查 callerpc : sys.GetCallerPC() racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapiterinit)) } ...}对此问题的解决办法是在 newproc1 注入的代码里面避免使用 map 数据结构。 七、总结 以上就是 Go 自动插桩工具在使用 go build -race 时出现崩溃的分析全过程。通过对崩溃内容和调用链的排查我们找到了产生问题的根本原因以及相应的解决方案。这将有助于我们在理解运行时机制的基础上更加谨慎地编写注入到运行时的代码。 参考链接 [01] Go 自动插桩开源项目 https://github.com/alibaba/opentelemetry-go-auto-instrumentation [02] 阿里云 ARMS Go Agent 商业版 https://help.aliyun.com/zh/arms/tracing-analysis/monitor-go-applications/ [03] Go 竞态检查 https://go.dev/doc/articles/race_detector
http://www.hkea.cn/news/14358936/

相关文章:

  • 外贸网站谷歌推广做网站不给提供ftp
  • 商业摄影网站福州大型网站建设
  • 一个人做网站现实吗滨州网站设计
  • 奖励软件下载网站网站后台维护费用
  • 仿朋友圈网站建设成都有哪些网站建设的公司
  • 网站怎么做英文版的it培训机构费用
  • 网站建设域名申请挂马网站教程
  • 深圳华强北营业时间网站页面优化方法有哪些
  • 济南做网站费用南海营销网站建设
  • 网站建设龙头企业网站建设 济南
  • 网站架构图用什么做织梦模板国外网站
  • 中扶建设网站做sns网站要多大空间
  • 网站建设与管理自考本全国卷网页设计与制作教程书
  • 手机网站编程语言商城网站建设论坛
  • 7年级微机课做网站的软件社区微网站建设方案
  • 好的文化网站模板下载psd模板免费下载网站
  • 建设通网站上线网上商城平台建设
  • 襄阳做公司网站的软件公司学历提升图片
  • 专门做985招聘信息的网站地方志网站群建设
  • 网站页面设计特点app拉新推广平台代理
  • 网站做301跳转的好处dede网站模板下载
  • 学校建设网站的作用wordpress添加语系
  • 手表网站错误怎么办苏州网站建设情况
  • 计算机专业论文网站开发做商城网站技术要点
  • 凯里市住房和城乡建设局网站html静态网页制作代码免费
  • 知名营销网站车网站建设策划
  • 做网站一般是什么工作商标备案查询官网
  • 河南省台前县建设局网站摄影网站网络促销方式
  • 网站框架设计图做网站排名公司推荐
  • 外贸网站cms系统做的好的家装网站