在生产环境中保持可调试性的同时优化构建 C++ 程序的最佳实践

Best practice to build C++ program with optimization while keeping debugability in production env

我正在编写一个将部署到 *nix 系统 (Linux/macOS) 的 C++ 服务器程序。程序在生产环境中有时会遇到segfault,我想收集一些core dump文件之类的信息。但我不知道这样做的最佳做法是什么:

  1. 我想让程序表现最好。
  2. 如果真的发生了,我想离线分析生产环境中的核心转储。

我了解到有些事情我可以尝试:

  1. CMAKE_BUILD_TYPERelWithDebInfoRelease,但它们似乎有不同的优化级别。所以我假设 RelWithDebInfo build 的性能不如 Release build。 (“RelWithDebInfo 使用 -O2,但 Release 使用 -O3“根据此处
  2. objectcopy/strip 等工具允许您从二进制文件 (How to generate gcc debug symbol outside the build target?)
  3. 中删除调试信息
  4. 处理 SIGSEGV 信号时打印堆栈跟踪 (How to automatically generate a stacktrace when my program crashes)

我是部署生产 C++ 服务器程序的新手,我想知道以下问题的答案:

  1. 在这种情况下建议使用什么构建类型,RelWithDebInfoRelease
  2. 相比于选择不同的构建类型,什么时候需要使用像strip这样的工具?
  3. 如果我为生产部署创建一个 Release 构建二进制文件,当 Release 构建在生产环境中生成一个核心转储时,我以后可以使用相同版本的源代码来构建吗RelWithDebInfo 二进制文件并使用(gdb + RelWithDebInfo 二进制文件 + Release 构建核心转储)进行核心转储分析?
  4. 在生产环境中打开核心转储是否很常见?如果这不是一个好的做法,那么收集故障排除信息、崩溃时打印堆栈跟踪的推荐方法是什么?

一般来说,我想知道如何建议为生产构建 C++ 程序,以便在我仍然能够对其进行故障排除的同时对其进行最佳优化。非常感谢。

这将是一个相当笼统的回答。

  1. 如果您仍然遇到可靠性问题,请选择 RelWithDebInfo。或者,您可以覆盖 -O2 优化,让编译器一直优化。
  2. 存在调试信息时,您需要strip。这不会更改实际执行的任何内容,但会删除使调试更容易的内容。您仍然可以调试剥离的可执行文件,但更难理解发生了什么。
  3. 否,由于不同的优化级别。如果两者之间的唯一区别是一个被剥夺了另一个没有,那么是的。但是随着优化级别的不同,生成的程序集实际上会有所不同。
  4. 通常不建议在生产环境中启用核心转储,主要是出于安全原因。例如,core dump 可能包含明文密码、会话令牌等,这些都是其他人不应该看到的东西。如果您可以完全控制 运行ning 所在的机器,那么这个问题就会小一些。另一个问题是磁盘 space 的使用。核心转储可能很大,具体取决于您的程序正在做什么。如果你有固定的核心文件名那么至少会有多个文件,但是如果你有一个包含时间戳and/or PID的名称,那么你可以有多个文件,每个文件都占用很多(意思是nGB) space。这可能会再次导致问题。

一般规则是(或应该是)您认为发布环境是有敌意的。有时是这样,有时则不然——这里不能通用,因为只有您知道自己的具体情况。

我总是部署完全优化的东西。如果程序特别有问题,我只会包括调试信息,因为它可以很容易地 运行 或使用 gdb.

附加到它

完全优化的缺点是有时看起来与您编写的代码有点不同。事情的顺序可能会改变,有些事情可能根本不会发生,并且您可能会观察到某些函数并不真正作为适当的独立函数存在,因为编译器认为它们最好被内联。这些是我观察到的变化,但可能还有其他变化。

最近我了解到有像Google Breakpad这样的工具可以生成minidump格式的崩溃报告,可以在生产环境中收集和分析。我还没有尝试过,但它可能对这个确切的目的有用。