你正在使用的Go版本是什么(go version
)?
$ go version
tip
这个问题在最新的版本中是否会重现?
是的
原始失败记录在这里 cockroachdb/cockroach#62979
我打开这个issue是为了获取关于这个问题的一些反馈和可能的解决方案。我知道社区中还有其他与cgo堆栈展开相关的报告,而这个问题就属于这类问题。
在这种情况下,问题出在一个C++信号处理器调用libc的backtrace函数,中断可以在Go代码或非Go代码中发生。可以在这里找到添加此功能的地方 cockroachdb/cockroach@957b4bd 。
对backtrace的调用在ppc64le上会导致SEGV,这并不令人意外,因为backtrace期望堆栈帧包含回链指针(根据PPC64 ABI)。但是,由于Go不维护回链指针,当backtrace尝试展开堆栈时,它最终会尝试使用一些不是指针的东西作为指针,从而导致SEGVs。
我看到过几个与cgo堆栈展开相关的其他问题,尤其是一个提到了使用cgosymbolizer作为解决方案的问题。看起来在这种情况下,cgosymbolizer可以在信号处理器内部使用,以在涉及Go代码时提供所需的堆栈信息。
@ianlancetaylor@cherrymui 这看起来像是一个可行的解决方案吗?创建一个Go信号处理器,然后使用cgosymbolizer来转储堆栈?
@pmur
7条答案
按热度按时间6jjcrrmo1#
这个在旧版本的Go中也能复现吗,还是这是新的?
根据你的分析,这对旧版本的Go也是适用的,这就是为什么我把它放在待办事项的原因。如果是新的,我们可能需要在这个版本中处理它。
4smxwvx52#
积压是可以的,但我认为这从未起作用过。我不指望在这个版本中解决这个问题,但试图了解是否应该这样工作。在我看来,他们正在做一些可能与Go不兼容的事情。如果是这样,是否有另一种方法可以编写它使其正常工作,或者至少在出现问题的平台上不会出现段错误(SEGV)。
以下是我理解的代码开始出错的摘要:
有一些C代码用于输出进程的所有堆栈。
创建一个C信号处理器SIGRTMIN,该处理器调用libc的backtrace函数。
一个辅助函数读取/proc/self/task以创建进程中所有线程的列表。然后它遍历列表并使用rt_tgsigqueueinfo向每个线程发送SIGRTMIN。
如果有一个接收到信号的Go线程,则调用C++信号处理器并在libc的backtrace中获得SEGV。
有几个似乎可疑的地方(尽管这在x86上“有效”),我不知道它们是否直接导致了问题:
如果我编写一个使用cgo调用backtrace的程序,这不会发生SEGV。Go代码将在调用C代码之前调用ascgocall,这将清除backtrace期望的位置。由于它是空的,backtrace将在此点停止。在Go代码运行并被中断的情况下,C++信号处理器获得控制权,中间没有像ascgocall这样的东西来指示backtrace应该停止。
我正在尝试为这个问题创建一个更小、更简单的复现器。
dphi5xsq3#
通过对#46286示例中的一些细微更改,可以简单地重现ppc64le的backtrace崩溃。将明显的段错误代码替换为调用backtrace,并用足够长的sleep替换死锁。
mbjcgjjk4#
通过对#46286示例中的一些细微更改,可以简单地重现ppc64le的backtrace崩溃。将明显的段错误代码替换为调用backtrace,并用足够长的sleep替换死锁。我的意思是,重现器应该有多个Go和非Go线程,其中向所有线程发送信号以生成类似于原始问题中的backtrace类型的信息。
mpgws1up5#
backtrace
函数并非官方的异步信号安全,但实际上它是安全的。为了让它在Go代码中正常工作,我想我们必须更改Go ABI以使用标准的栈帧布局。这似乎是一个好主意,就像我们在x86上使用帧指针一样。但我不知道这会有多大难度。我同意使用cgosymbolizer应该是可行的。
wvyml7n56#
为了让它在Go代码中正常工作,我想我们必须改变Go ABI以使用标准的堆栈帧布局。这似乎是一个好主意,就像我们在x86上使用帧指针一样。但我不知道这有多难。
如果我们能做到这一点,那就太好了。还有其他一些工具期望PPC64 ABI,所以不能正确工作。我一直以为这是不可行的,因为兼容性问题。由于工作和风险,下一个版本需要调查这个问题。
我同意使用cgosymbolizer应该是可行的。
根据我的理解,最好有一个Go信号处理器来为运行Go的线程转储堆栈,并从该信号处理器中使用cgosymbolizer。这是不是正确的方法?
gudnpqoy7#
根据我的理解,最好有一个Go信号处理器来转储运行Go的线程的堆栈,并从该信号处理器中使用cgosymbolizer。这是不是正确的方法?
我认为这应该可以工作,是的。