【问题标题】:Methods/Tools for solving a Mystery Segfault while running on condor在 condor 上运行时解决神秘 Segfault 的方法/工具
【发布时间】:2010-09-10 01:32:39
【问题描述】:

我正在编写一个跨计算集群运行的 C 应用程序(使用 condor)。我尝试了很多方法来揭示有问题的代码,但都无济于事。

线索:

  • 平均而言,当我在 15 台机器上运行代码 2 天时,会出现两到三个段错误(信号 11)。
  • 当我在本地运行代码时,我没有遇到段错误。我在家用机器上运行了将近 3 周。

尝试:

  • 我在 valGrind 中在本地运行了四天的代码,没有出现内存错误。
  • 我通过定义自己的信号处理程序捕获了段错误信号,以便可以输出一些程序状态。
  • 现在,当发生段错误时,我可以使用回溯打印出当前堆栈。
  • 我可以打印出变量值。
  • 我创建了一个设置为当前行号的变量。
  • 还尝试注释掉代码块,希望如果问题消失,我会发现段错误。

遗憾的是,输出的行号是相当随机的。我不完全确定我可以用堆栈跟踪做什么。我假设它只记录发生段错误的函数的地址是否正确?

怀疑:

  • 我怀疑 condor 用于跨机器移动作业的检查点系统对内存损坏更敏感,这就是我在本地看不到它的原因。
  • 索引已被错误破坏,并且这些索引导致了段错误。这可以解释段错误发生在相当随机的行号上的事实。

更新

对此进行了更多研究,我发现了以下链接:

更新 2

Greg 建议查看 condor 日志并“将段错误与 condor 从检查点重新启动可执行文件的时间相关联”。查看日志,所有段错误都在重新启动后立即发生。当作业从一种类型的机器切换到另一种类型时,所有故障似乎都会发生。

更新 3

段错误是由主机之间的差异引起的,通过将 condor 提交文件中的“requiremets”字段设置为问题完全消失。

可以单独设置机器:

requirements = machine == "hostname1" || machine == "hostname2"

或一整类机器:

requirements = classOfMachinesName

查看需求示例here

【问题讨论】:

  • 最近的 gcc 有挡泥板选项来做一些运行时检查。
  • 这似乎更像是一个多进程践踏问题而不是内存损坏问题(尤其是因为您无法在家中重现它)
  • @Mark ,我认为操作系统会阻止这种情况?不要进程有不同的地址空间。
  • @e5,我想我的意思是线程,我不完全了解环境,但症状类似于我之前看到的由某些比赛/计时条件导致的问题——持续失败但不断变化职位
  • @E5,好吧,该死。有很多简单的方法可以获得不可重现的崩溃。另一方面,调试单线程代码要容易得多。我怀疑你实际上可以对你的集群使用 gdb 的提示将是最有效的。

标签: c memory-corruption condor


【解决方案1】:

如果可以的话,用调试编译,然后在 gdb 下运行。 或者,将核心转储并加载到调试器中。

mpich 有内置调试器,或者你可以购买商业并行调试器。

然后你可以单步调试代码来查看调试器中发生了什么

http://nmi.cs.wisc.edu/node/1610

http://nmi.cs.wisc.edu/node/1611

【讨论】:

  • 我无法在 gdb 下运行它,因为我只在集群上运行时才看到问题,并且必须运行两天才能出现问题。
  • @e5 你可以在集群上使用gdb,非常灵活的工具。
  • 怎么样?不是所有在 condor 标准上运行的代码都必须重新链接吗?愿意链接到某人这样做的示例吗?
  • 鲤鱼非常感谢,也许您应该将其发布为您的答案的一部分。
【解决方案2】:

您可以在发生段错误时创建核心转储吗?然后,您可以调试此转储以尝试找出代码崩溃时的状态。

看看是什么指令导致了故障。它甚至是一个有效的指令还是你试图执行数据?如果有效,它试图访问什么内存?这个指针是从哪里来的。您需要缩小故障位置(堆栈损坏、堆损坏、未初始化的指针、访问无效内存)。如果是损坏,请查看损坏区域中是否有任何迹象数据(指向符号的指针、看起来像结构中的某些东西的数据……)。您的内存分配器可能已经内置了调试某些损坏的功能(请参阅 Linux 上的 MALLOC_CHECK_ 或 Mac OS 上的 MallocGuardEdges)。这些情况的常见情况是使用已释放 () 的内存,因此记录您的 malloc() / free() 对可能会有所帮助。

【讨论】:

  • 我应该注意我没有使用 malloc 或 free。该程序相当简单,只使用一些全局定义的变量。我会考虑获取核心转储,但目前没有提供。
【解决方案3】:

如果您使用 condor_compile 工具将您的代码与 condor 检查点代码重新链接,它会做一些与普通链接不同的事情。最重要的是,它静态链接您的代码,并使用它自己的 malloc。另一个很大的不同是,condor 将在一台外国机器上运行它,那里的环境可能与您预期的会导致问题的环境大相径庭。

condor_compile 生成的可执行文件可以在 condor 系统之外作为独立的二进制文件运行。如果你在本地运行从 condor_compile 发出的二进制文件,在 condor 之外,你还会看到段错误吗?

如果没有,您能否将段错误与 condor 从检查点重新启动可执行文件的时间关联起来(用户日志会告诉您何时发生这种情况)。

【讨论】:

  • 段错误与 condor 重新启动的时间无关。查看 gdb 中的核心文件,导致段错误的行和操作是完全有效的,并且在全局数组的范围内。我在所有变量周围添加了 cookie,但它们都没有损坏。检查我的所有变量(全局变量和其他变量)我没有发现损坏。
  • 哎呀! segfaults 确实与 condor 重新启动的时间相关!我认为是秃鹰造成的问题。
  • 好的,这就是进展。您还可以尝试检查点并在 Condor 之外重新启动可执行文件,以查看检查点代码是否出错。您可能还想在 condor-users 电子邮件列表上发帖以获得更直接的支持。
  • 上面你说你有不止一种类型的机器——你能进一步解释一下吗?有多少机器,有多少种类?如果有一些有问题的机器类型,您可以使用需求表达式限制 condor 作业不要在那里运行。
  • 我不确定这些机器到底有什么不同,但它们有不同的主机名。中断的主机名类似于 y1,y2,y3... 而有效的软管名称是 x1,x2,3。两组都运行相同的 linux 内核(根据 nmap -O)。
【解决方案4】:

你已经尝试了我想的大部分。我唯一建议的另一件事是开始添加大量日志记录代码,并希望您能缩小错误发生的范围。

【讨论】:

    【解决方案5】:

    你没有说的一件事是你有多大的灵活性来解决问题。 例如,您能否让系统停止并运行您的应用程序? 还有这些崩溃的解决有多重要?

    我假设大部分情况下您都会这样做。这可能需要大量资源。

    短期步骤是为每个变量添加大量“断言”(半手写) 确保它在你不想要的时候没有改变。随着您经历长期过程,这可以继续发挥作用。

    长期 - 尝试在两个集群(可能是您的家用计算机和虚拟机)上运行它。 你还看到段错误吗?如果不增加集群大小,直到您开始看到段错误。

    以最低配置运行它(以获得段错误)并记录所有输入直到崩溃。使用您记录的输入自动运行系统,并对其进行调整,直到您可以始终以最少的输入获得崩溃。

    那时环顾四周。如果您仍然找不到错误,那么您将不得不再次询问您在这些运行中收集的一些额外数据。

    【讨论】:

      猜你喜欢
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-08-10
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多