profileLoop
通过 mp.threadLock
部分同步到 unminit
,但在 _SuspendThread
和 profilem
之前丢弃了 threadLock
。
因此,性能分析可能会与 dropm
竞争,导致 profilem
在不再被 Go 使用的 "额外" M 上运行,其先前的线程正在运行 C 代码。
从理论上讲,这应该导致 gFromSP
返回 nil(因为逻辑上不应该有任何 G,因为这不是 Go 线程),这可能会导致 sigprof
崩溃,因为它假设 gp != nil
是有效的。然而,在实践中,SP 可能仍然落在 mp.g0
栈范围内,最终没有崩溃,尽管没有意义的性能分析样本。
我发现可以通过一个 C 线程反复调用 Go 函数并检查 mp.thread
是否仍然设置来轻松检测这种条件。
5条答案
按热度按时间ldxq2e6h1#
看起来
preemptM
遇到了类似的问题。它使用mp.preemptExtLock
与 "外部代码" 同步,这是由cgocall
设置的,但不是由dropm
/unminit
设置的。像profileLoop
一样,preemptM
通过mp.threadLock
无法与unminit
完全同步。因此,我认为preemptM
可能会在unminit
之后尝试抢占一个 C 线程,该线程可能会调用ExitProcess
。cc @aclements
9o685dep2#
我在这里看到了几个不同的选项:
mp.threadLock
,直到_SuspendThread
完成(即在_GetThreadContext
之后),这样unminit
在停止线程之前无法运行。mp.threadLock
上的竞争可能性。一个preemptM
调用可能需要等待很长时间才能让profileLoop
解锁,然后才能继续。preemptM
无论如何都会暂停线程,所以理论上等待profileLoop
也只会使preemptM
花费两倍的时间。preemptM
将立即拒绝抢占。unminit
上清除sigprof
并跳过gFromSP
/preempt 如果needm
不能匹配到 G。mp.g0.stack
一起抢夺 M 并设置preemptM
的竞争,而这可能甚至不是将 M 分配给我们正在分析的相同线程。mp.preemptExtLock
,我们可以在unminit
中设置profilem
,这将使其跳过未初始化的 Ms 作为“外部代码”。t3psigkw3#
我认为4是一个很好的抢占策略。不确定关于分析,也许我们可以做到3。也许我们可以将设置/取消设置堆栈边界放在同一个锁中。或者我们可以在线程挂起后重新检查mp.thread?(是否存在任何ABA问题?)
不确定持有锁暂停线程的含义。我没有仔细考虑过,但听起来有点危险(可能导致死锁)。也许没问题。
d8tt03nd4#
2022-09-05T08:08:24-af7f417/windows-amd64-longtest
这是否是完全相同的问题?它看起来非常相似。在
profilem
中发生了空指针解引用。eqzww0vc5#
我相信那是#54885。