简体   繁体   English

在调试模式下不存在发​​布版本中的错误的常见原因

[英]Common reasons for bugs in release version not present in debug mode

仅在发布编译模式下出现但在调试模式下不出现的错误和异常程序行为的典型原因是什么?

Many times, in debug mode in C++ all variables are null initialized, whereas the same does not happen in release mode unless explicitly stated.很多时候,在 C++ 的调试模式下,所有变量都是空初始化的,而除非明确说明,否则在发布模式下不会发生同样的情况。

Check for any debug macros and uninitialized variables检查任何调试宏和未初始化的变量

Does your program uses threading, then optimization can also cause some issues in release mode.您的程序是否使用线程,那么优化也会导致发布模式出现一些问题。

Also check for all exceptions, for example not directly related to release mode but sometime we just ignore some critical exceptions, like mem access violation in VC++, but the same can be a issue at least in other OS like Linux, Solaris.还要检查所有异常,例如与发布模式没有直接关系,但有时我们只是忽略一些关键异常,例如 VC++ 中的内存访问冲突,但至少在其他操作系统(如 Linux、Solaris)中同样可能存在问题。 Ideally your program should not catch such critical exceptions like accessing a NULL pointer.理想情况下,您的程序不应捕获诸如访问 NULL 指针之类的关键异常。

一个常见的陷阱是在 ASSERT 中使用具有副作用的表达式。

I've been bitten by a number of bugs in the past that have been fine in Debug builds but crash in Release builds.过去,我被许多错误所困扰,这些错误在 Debug 版本中很好,但在 Release 版本中会崩溃。 There are many underlying causes (including of course those that have already been summarised in this thread) and I've been caught out by all of the following:有许多根本原因(当然包括本主题中已经总结的那些),并且我已经被以下所有原因所吸引:

  • Member variables or member functions in an #ifdef _DEBUG , so that a class is a different size in a debug build. #ifdef _DEBUG中的成员变量或成员函数,以便类在调试版本中具有不同的大小。 Sometimes #ifndef NDEBUG is used in a release build有时#ifndef NDEBUG用于发布版本
  • Similarly, there's a different #ifdef which happens to be only present in one of the two builds同样,有一个不同的#ifdef恰好只出现在两个版本之一中
  • The debug version uses debug versions of the system libraries, especially the heap and memory allocation functions调试版本使用系统库的调试版本,尤其是堆和内存分配函数
  • Inlined functions in a release build发布版本中的内联函数
  • Order of inclusion of header files.头文件的包含顺序。 This shouldn't cause problems, but if you have something like a #pragma pack that hasn't been reset then this can lead to nasty problems.这应该不会导致问题,但是如果您有诸如#pragma pack类的东西尚未重置,那么这可能会导致严重的问题。 Similar problems can also occur using precompiled headers and forced includes使用预编译头和强制包含也会出现类似问题
  • Caches: you may have code such as caches that only gets used in release builds, or cache size limits that are different缓存:您可能有一些代码,例如仅在发布版本中使用的缓存,或不同的缓存大小限制
  • Project configurations: the debug and release configurations may have different build settings (this is likely to happen when using an IDE)项目配置:调试和发布配置可能有不同的构建设置(使用 IDE 时可能会发生这种情况)
  • Race conditions, timing issues and miscellanous side-effects occurring as a result of debug only code由于仅调试代码而发生的竞争条件、计时问题和其他副作用

Some tips that I've accumulated over the years for getting to the bottom of debug/release bugs:我多年来积累的一些用于深入调试/发布错误的技巧:

  • Try to reproduce anomalous behaviour in a debug build if you can, and even better, write a unit test to capture it如果可以,尝试在调试版本中重现异常行为,甚至更好的是,编写一个单元测试来捕获它
  • Think about what differs between the two: compiler settings, caches, debug-only code.想想两者之间的不同之处:编译器设置、缓存、仅调试代码。 Try to minimise those differences temporarily尝试暂时最小化这些差异
  • Create a release build with optimisations switched off (so you're more likely to get useful data in the debugger), or an optimised debug build.创建关闭优化的发布版本(这样您更有可能在调试器中获得有用的数据)或优化的调试版本。 By minimising the changes between debug and release, you're more likely to be able to isolate which difference is causing the bug.通过最小化调试和发布之间的更改,您更有可能能够隔离导致错误的差异。

Other differences might be:其他差异可能是:

  • In a garbage-collected language, the collector is usually more aggressive in release mode;在垃圾收集语言中,收集器通常在释放模式下更积极;
  • Layout of memory may often be different;内存的布局可能经常不同;
  • Memory may be initialized differently (eg could be zeroed in debug mode, or re-used more aggressively in release);内存可能会以不同的方式初始化(​​例如,可以在调试模式下归零,或者在发布时更积极地重新使用);
  • Locals may be promoted to register values in release, which can cause issues with floating point values.局部变量可能会被提升为在 release 中注册值,这可能会导致浮点值出现问题。

是的!,如果您有条件编译,则可能存在计时错误(优化的发布代码节,未优化的调试代码)、内存重用与调试堆。

It can, especially if you are in the C realm.它可以,特别是如果您在 C 领域。

One cause could be that the DEBUG version may add code to check for stray pointers and somehow protect your code from crashing (or behave incorrectly).一个原因可能是 DEBUG 版本可能会添加代码来检查杂散指针并以某种方式保护您的代码免于崩溃(或行为不正确)。 If this is the case you should carefully check warnings and other messages you get from your compiler.如果是这种情况,您应该仔细检查从编译器获得的警告和其他消息。

Another cause could be optimization (which is normally on for release versions and off for debug).另一个原因可能是优化(通常对发布版本打开,在调试时关闭)。 The code and data layout may have been optimized and while your debugging program just was, for example, accessing unused memory, the release version is now trying to access reserved memory or even pointing to code!代码和数据布局可能已经优化,而您的调试程序只是,例如,访问未使用的内存,而发布版本现在正在尝试访问保留内存甚至指向代码!

EDIT: I see other mentioned it: of course you might have entire code sections that are conditionally excluded if not compiling in DEBUG mode.编辑:我看到其他人提到了它:当然,如果不在调试模式下编译,您可能有整个代码部分被有条件地排除在外。 If that's the case, I hope that is really debugging code and not something vital for the correctness of the program itself!如果是这样,我希望这真的是调试代码,而不是对程序本身的正确性至关重要的东西!

The CRT library functions behave differently in debug vs release (/MD vs /MDd). CRT 库函数在调试与发布(/MD 与 /MDd)中的行为不同。

For example, the debug versions often prefill buffers you pass to the indicated length to verify your claim.例如,调试版本通常会预填充您传递给指定长度的缓冲区以验证您的声明。 Examples include strcpy_s , StringCchCopy , etc. Even if the strings terminate earlier, your szDest better be n bytes long!例子包括strcpy_sStringCchCopy ,等等。即使琴弦较早终止,您szDest最好是N久字节!

Sure, for example, if you use constructions like当然,例如,如果您使用类似的结构

#if DEBUG

//some code

#endif

In a non-void function, all execution paths should end with a return statement.在非 void 函数中,所有执行路径都应以 return 语句结束。

In debug mode, if you forget to end such a path with a return statement then the function usually returns 0 by default.在调试模式下,如果您忘记用 return 语句结束这样的路径,那么该函数通常默认返回 0。

However, in release mode your function may return garbage values, which may affect how your program runs.但是,在发布模式下,您的函数可能会返回垃圾值,这可能会影响程序的运行方式。

You'd need to give a lot more information, but yes, it's possible.您需要提供更多信息,但是是的,这是可能的。 It depends what your debug version does.这取决于您的调试版本的作用。 You may well have logging or extra checks in that that don't get compiled into a release version.您可能会进行日志记录或额外检查,这些检查不会被编译到发布版本中。 These debug only code paths may have unintended side effects which change state or affect variables in strange ways.这些仅用于调试的代码路径可能会产生意想不到的副作用,这些副作用会以奇怪的方式改变状态或影响变量。 Debug builds usually run slower, so this may affect threading and hide race conditions.调试构建通常运行速度较慢,因此这可能会影响线程和隐藏竞争条件。 The same for straight forward optimisations from a release compile, it's possible (although unlikely these days) that a release compile may short circuit something as an optimisation.与发布编译的直接优化相同,发布编译可能(尽管现在不太可能)将某些内容作为优化短路。

Without more details, I will assume that "not OK" means that it either does not compile or throws some sort of error at runtime.没有更多细节,我会假设“不正常”意味着它要么不编译,要么在运行时抛出某种错误。 Check if you have code that relies on the compilation version, either via #if DEBUG statements or via methods marked with the Conditional attribute.检查您是否有依赖编译版本的代码,通过#if DEBUG语句或通过标记有Conditional属性的方法。

在 .NET 中,即使您不使用#if DEBUG类的条件编译,编译器在发布模式下的优化仍然比在调试模式下更加自由,这也可能导致仅发布错误。

There are compiler optimizations that can break valid code because they are too aggressive.有些编译器优化可能会破坏有效代码,因为它们过于激进。

Try compiling your code with less optimization turned on.尝试在启用较少优化的情况下编译您的代码。

That is possible, if you have conditional compilation so that the debug code and release code are different, and there is a bug in the code that is only use in the release mode.这是可能的,如果你有条件编译,使得调试代码和发布代码不同,并且代码中有一个只在发布模式下使用的错误。

Other than that, it's not possible.除此之外,这是不可能的。 There are difference in how debug code and release code are compiled, and differences in how code is executed if run under a debugger or not, but if any of those differences cause anything other than a performance difference, the problem was there all along.调试代码和发布代码的编译方式有所不同,代码在调试器下运行与否的执行方式也有所不同,但如果这些差异中的任何一个导致性能差异以外的任何其他原因,那么问题就一直存在。

In the debug version the error might not be occuring (because the timing or memory allocation is different), but that doesn't mean that the error is not there.在调试版本中,错误可能不会发生(因为时间或内存分配不同),但这并不意味着错误不存在。 There may also be other factors that are not related to the debug mode that changes the timing of the code, causing the error to occur or not, but it all boils down to the fact that if the code was correct, the error would not occur in any of the situations.也可能还有其他与调试模式无关的因素改变了代码的时序,导致错误发生与否,但归根结底,如果代码正确,错误就不会发生在任何情况下。

So, no, the debug version is not OK just because you can run it without getting an error.所以,不,调试版本不能仅仅因为您可以运行它而不会出错。 If an error occurs when you run it in release mode, it's not because of the release mode, it's because the error was there from the start.如果在发布模式下运行时发生错误,那不是因为发布模式,而是因为错误从一开始就存在。

It's possible.这是可能的。 If it happens and no conditional compilation is involved, than you can be pretty sure that your program is wrong, and is working in debug mode only because of fortuitous memory initializations or even layout in memory!如果它发生并且不涉及条件编译,那么您可以非常确定您的程序是错误的,并且仅由于偶然的内存初始化甚至内存布局而在调试模式下工作!

I just experienced that when I was calling an assembly function that didn't restored the registers' previous values.我刚刚在调用未恢复寄存器先前值的汇编函数时遇到过这种情况。

In the "Release" configuration, VS was compiling with /O2 which optimizes the code for speed.在“发布”配置中,VS 使用 /O2 进行编译,这优化了代码的速度。 Thus some local variables where merely mapping to CPU registers (for optimization) which were shared with the aforementioned function leading to serious memory corruption.因此,一些局部变量仅映射到与上述函数共享的 CPU 寄存器(用于优化),从而导致严重的内存损坏。

Anyhow see if you aren't indirectly messing with CPU registers anywhere in your code.无论如何,看看您是否没有在代码中的任何地方间接弄乱 CPU 寄存器。

I remember while ago when we were building dll and pdb in c/c++.我记得不久前我们在 c/c++ 中构建 dll 和 pdb。

I remember this:我记得这个:

  • Adding log data would sometime make the bug move or disappear or make a totally other error appears (so it was not really an option).添加日志数据有时会使错误移动或消失,或者出现完全其他的错误(因此这不是一个真正的选择)。
  • Many of these errors where related to char allocation in strcpy and strcat and arrays of char[] etc...其中许多错误与 strcpy 和 strcat 中的 char 分配以及 char[] 数组等有关...
  • We weeded some out by running bounds checker and simply fixing the memory alloc/dealloc issues.我们通过运行边界检查器并简单地修复内存分配/释放问题来清除一些。
  • Many times, we systematically went through the code and fixed a char allocation.很多时候,我们系统地检查了代码并修复了字符分配。
  • My two cents is that it is related to memory allocation and management and constraints and differences between Debug mode and release mode.我的两点是它与内存分配和管理以及调试模式和释放模式之间的约束和差异有关。

And then kept at going through that cycle.然后继续经历那个循环。

We sometimes, temporarily swapped release for debug versions of dlls, in order not to hold off production, while working on these bugs.我们有时会临时交换 dll 的调试版本的发行版,以免在处理这些错误时推迟生产。

Another reasons could be DB calls.另一个原因可能是数据库调用。 Are you saving and updating same record multiple times in same thread, sometimes for updating.您是否在同一线程中多次保存和更新同一记录,有时是为了更新。 Its possible the update failed or didnt work as expected because the previous create command was still processing and for update, the db call failed to find any record.更新失败或未按预期工作可能是因为之前的创建命令仍在处理中,而对于更新,db 调用未能找到任何记录。 this wont happen in debug as debugger makes sure to complete all pending tasks before landing.这不会在调试中发生,因为调试器确保在登陆前完成所有挂起的任务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM