【问题标题】:The notorious printf fix臭名昭著的 printf 修复
【发布时间】:2015-03-21 01:07:45
【问题描述】:

根据我的经验,我在使用 printf(或任何其他标准输出日志记录)进行调试时遇到了一些奇怪的行为。

行为 1:

一个常见的场景是在多线程应用程序中使用 printf 以找出某些错误发生的原因,并使用 printf 突然“修复”错误(积极调用的 ofc printfs,导致巨大的输出)。

在这种情况下,我认为 printf 会增加一些延迟,因此可能会有一些低优先级的线程无法获得 CPU,因此我开始朝那个方向寻找。

我关注 printf 修复奇迹的另一个方向是同步,因为我推测对 printf 的调用虽然是多线程的,但在系统后面是同步的,因此使用 printf 的不同线程通过等待彼此来在它们之间同步完成写入 I/O 缓冲区。

Q1:我对第一种情况的两个假设是否正确?

Q2:当这种情况发生时,我应该考虑其他方向吗?

行为 2:

这种情况很少发生,但一旦碰到它,即使是高级开发人员也会质疑自己,我非常感谢对此的解释。

它是这样的:

  • 代码不起作用...(清理、编译、运行)
  • 代码仍然不起作用,所以你添加一个 printf 来查看原因(清理、编译、运行)
  • 代码开始正常工作......您删除了之前添加的 printf(清理、编译、运行)
  • 代码现在可以正常工作了!!!!!!!!!! (挠头,难以置信地盯着看)。

在实践中,我使用这种方法的次数超过了一次,这种修复 CPU 的方法可能不止一次地钉住错误:Android "cpu may be pegged" bug

它实际上工作得很好,以至于它成为了一个已知的“修复”(如果它从第一次尝试就不起作用,你只需重复这个过程直到它消失)。

请注意,代码已被正确清理,它绝不是与旧编译对象链接的问题。

最流行的猜测之一是编译代码不同的事实,原因不明(编译器是否根据某个文件的行有一些随机性,包括空格?)。

Q3:这种行为可能是什么原因(我也愿意猜测)?尽管代码相同,编译器可以生成不同的程序集吗?

请注意,我所说的项目非常大,有多个静态库,因此这些行为在小代码 sn-ps 上是不可复制的(尽管我听说场景 2 发生在单个文件上程序)。

【问题讨论】:

  • 请一次只作为一个问题。 SO 意味着可搜索并将知识带给他人。
  • 看起来您的代码有问题,原因是同步错误。您的代码可能会根据不可预测的系统行为运行/失败,这与 printf 无关。您将不得不检查共享变量、程序员在您的代码中所做的任何类型的休眠或时序假设。
  • @Jens Gustedt 我理解你只发布一个问题的意思,但我相信这个问题可以给社区带来的一般知识是“为什么有时你似乎用 printf 解决问题”。我给出了两种情况,可能行为 1 和行为 2 的原因不同,但是从开发人员的 POV 来看,他刚刚输入了 printf,然后魔术发生了。

标签: c++ c multithreading io


【解决方案1】:

Q1:您可以通过从两个不同的printfs 中查找交错字符来判断printf 是否在幕后同步。如果没有交错,则printf 正在同步。我希望这比 CPU 占用更有可能解决问题。

Q2:我会寻找没有适当互斥保护的共享资源。

Q3:编译器在优化过程中可能会使用随机数。例如,如果编译器有 32 个变量和 8 个寄存器可将它们放入,它可能会“掷骰子”来确定将哪些变量放入寄存器。您可以通过禁用优化来测试这个理论。如果没有优化,输出应该是一致的。您可以通过比较二进制文件来测试那个理论。

【讨论】:

    【解决方案2】:

    printf() 的线程安全性在其他问题e.g. here for linux 中进行了讨论,还值得注意的是,任何外线函数调用都可能导致写回内存——引用 David Butenhoff 的话

    “实际上,大多数编译器不会尝试保留 调用外部函数的全局数据,因为它也是 很难知道例程是否可能以某种方式访问 数据的地址。”

    这两个方面都可能意味着由于未能正确使用同步指令而导致的未定义行为可能会通过调用printf() 被“屏蔽”(根据您的架构、问题的确切性质等不同程度的可靠)。

    如您所说,拨打printf 所花费的时间也会影响竞争条件产生不利影响的频率。

    关于重新编译程序修复错误:首先,如果错误首先是间歇性的,在一些变化之前而不是之后观察它并不一定证明任何因果关系。可能还有其他因素,例如由于防病毒扫描、备份计划、其他用户等其他因素导致的系统负载减少。其次,可执行文件可能不一样:编译器可能会注入类似递增的东西版本号或构建时间戳或其他一些系统数据 - 例如数据长度可能会对其他数据的对齐产生连锁影响,并产生许多微妙的后果。您的编译器也有可能使用地址随机化或其他一些技术,这又会影响数据对齐 - 以一种可能掩盖错误或改变性能的方式。

    【讨论】:

      猜你喜欢
      • 2017-10-03
      • 2023-01-27
      • 1970-01-01
      • 1970-01-01
      • 2012-11-18
      • 1970-01-01
      相关资源
      最近更新 更多