go 运行时:Windows配置文件可能在未初始化Ms后对其进行分析,

tquggr8v  于 4个月前  发布在  Go
关注(0)|答案(5)|浏览(43)

profileLoop 通过 mp.threadLock 部分同步到 unminit ,但在 _SuspendThreadprofilem 之前丢弃了 threadLock
因此,性能分析可能会与 dropm 竞争,导致 profilem 在不再被 Go 使用的 "额外" M 上运行,其先前的线程正在运行 C 代码。
从理论上讲,这应该导致 gFromSP 返回 nil(因为逻辑上不应该有任何 G,因为这不是 Go 线程),这可能会导致 sigprof 崩溃,因为它假设 gp != nil 是有效的。然而,在实践中,SP 可能仍然落在 mp.g0 栈范围内,最终没有崩溃,尽管没有意义的性能分析样本。
我发现可以通过一个 C 线程反复调用 Go 函数并检查 mp.thread 是否仍然设置来轻松检测这种条件。

ldxq2e6h

ldxq2e6h1#

看起来 preemptM 遇到了类似的问题。它使用 mp.preemptExtLock 与 "外部代码" 同步,这是由 cgocall 设置的,但不是由 dropm / unminit 设置的。像 profileLoop 一样,preemptM 通过 mp.threadLock 无法与 unminit 完全同步。因此,我认为 preemptM 可能会在 unminit 之后尝试抢占一个 C 线程,该线程可能会调用 ExitProcess
cc @aclements

9o685dep

9o685dep2#

我在这里看到了几个不同的选项:

  • 保持 mp.threadLock ,直到 _SuspendThread 完成(即在 _GetThreadContext 之后),这样 unminit 在停止线程之前无法运行。
  • 我认为这是最简单的方法,但确实会引入 mp.threadLock 上的竞争可能性。一个 preemptM 调用可能需要等待很长时间才能让 profileLoop 解锁,然后才能继续。
  • 另一方面,也许这没关系。在标准情况下, preemptM 无论如何都会暂停线程,所以理论上等待 profileLoop 也只会使 preemptM 花费两倍的时间。
  • 与 (1)相同,但添加一个原子标志,表示性能分析暂停正在进行中。如果设置了此标志,preemptM 将立即拒绝抢占。
  • unminit 上清除 sigprof 并跳过 gFromSP /preempt 如果 needm 不能匹配到 G。
  • 我不太喜欢这个,因为它仍然允许与 mp.g0.stack 一起抢夺 M 并设置 preemptM 的竞争,而这可能甚至不是将 M 分配给我们正在分析的相同线程。
  • 对于 mp.preemptExtLock ,我们可以在 unminit 中设置 profilem ,这将使其跳过未初始化的 Ms 作为“外部代码”。
  • 对于 x20n20 ,什么都不做?我们得到无意义的性能分析样本,可能会用空 G 导致 x20n20crash,但我们也可以完全跳过空 G 上的性能分析。
t3psigkw

t3psigkw3#

我认为4是一个很好的抢占策略。不确定关于分析,也许我们可以做到3。也许我们可以将设置/取消设置堆栈边界放在同一个锁中。或者我们可以在线程挂起后重新检查mp.thread?(是否存在任何ABA问题?)

不确定持有锁暂停线程的含义。我没有仔细考虑过,但听起来有点危险(可能导致死锁)。也许没问题。

d8tt03nd

d8tt03nd4#

2022-09-05T08:08:24-af7f417/windows-amd64-longtest
这是否是完全相同的问题?它看起来非常相似。在 profilem 中发生了空指针解引用。

相关问题