最近,我们收到了一位用户的错误报告:屏幕上的某些内容在我们的软件中显示不正确。不知何故,我们无法在我们的开发环境中重现这一点(Delphi 2007)。
经过进一步研究后,当<代码优化< 时,
这里有没有人在追捕这样的Heisenbug?在Delphi软件中通常会导致此类问题的任何特定构造或编码错误?你会开始寻找任何地方吗?
我也会以通常的方式开始调试整个事情,但任何特定于优化相关错误(*)的提示都会受到欢迎!
(*)注意:我并不是说优化器会将错误引起;我认为代码中某些不稳定的构造更有可能被优化器“推到边缘”。
当没有代码优化时,错误似乎归结为用零完全初始化的记录,并且当 优化时,包含一些随机数据的记录相同。在这种情况下,随机数据似乎会导致枚举类型包含无效数据(令我惊讶的是!)。
该解决方案最终涉及到代码深处某处的单元化本地记录变量。显然,如果没有优化,记录 重置(堆?),并且上的优化后,记录会被通常的垃圾填满。感谢大家的贡献 - 我一路上学到了很多东西!
答案 0 :(得分:12)
这种形式的错误通常是由无效的内存访问(读取未初始化的数据,读取缓冲区的末尾......)或线程竞争条件引起的。
前者将受到优化的影响,导致数据布局在内存中重新排列,和/或可能通过将新分配的内存初始化为某个值的调试代码;导致错误的代码“意外工作”。
由于优化级别之间的时间变化,后者将受到影响。前者通常更有可能。
如果你有一些自动方式使新分配的内存在传递给程序之前填充一些常量值,这会使崩溃消失或在调试版本中变得可重现,这将提供一个好点开始追逐事物。
答案 1 :(得分:6)
很可能是一个内存与注册问题:你可以在免费后依赖内存持久性运行 我建议在完全调试模式下使用FastMM4运行应用程序,以确保您的内存管理 在这种情况下非常有用的另一种(非自由)工具是Eurekalog。
我见过的另一件事:在调用一些外部代码(DLL,COM ......)时,FPU寄存器崩溃,而调试器一切正常。
答案 2 :(得分:3)
根据不同编译器设置包含不同数据的记录告诉我一件事:该记录未被显式初始化。
您可能会发现编译器优化标志的设置只是可能影响该记录内容的一个因素 - 对于任何未初始化的数据结构,您可以依赖的一件事是您不能依赖于结构的内容。
简单来说:
类成员数据初始化(为零)用于类的新实例
局部变量(在函数和过程中)和单元变量是初始化的,除了在一些特定的情况下:接口引用,动态数组和字符串,我认为(但需要检查)记录,如果它们包含一个或这些类型的更多字段将被初始化(字符串,接口引用等)。
如上所述的问题现在有点误导,因为你似乎很容易找到你的“海森堡”。现在的问题是如何处理它,答案只是显式初始化你的记录,这样你就不会依赖于编译器的任何行为或副作用,有时候会为你处理,有时也不会。 / p>
答案 3 :(得分:2)
特别是在纯粹的本土语言中,比如德尔福,你应该更加小心,不要滥用自由,以便能够投入任何东西。 IOW:有一点,我看到有人将类的定义(例如从RTL或VCL中的实现部分)复制到他自己的代码中,然后将原始类的实例复制到他的副本中。 现在,在升级原始类所在的库之后,您可能会遇到各种奇怪的东西。就像跳进错误的方法或缓冲区溢出一样。
还有使用有符号整数作为指针的习惯,反之亦然。 (而不是红衣主教) 只要您的进程只有2GB的地址空间,这就完美无缺。但是使用/ 3GB开关启动,你会看到许多应用程序开始疯狂。那些至少在某处做过“指针=有符号整数”的假设。 您的客户使用64位Windows?有可能,他可能会为32Bit应用程序提供更大的地址空间。如果没有这样的测试系统,调试非常困难。
然后,有竞争条件。 就像有2个线程,其中一个非常非常慢。所以你本能地假设它永远是最后一个,因此没有代码可以处理“Captn slow”首先完成的场景。 底层技术的变化可能使这些假设非常错误,确实非常快。 看看即将推出的基于Flash的超级超级快速服务器存储。 可以每秒读写千兆字节的系统。假设IO内容比内存中的某些计算速度慢得多的应用程序很容易在这种快速存储上失败。
我可以继续,但我现在要跑了...... 干杯
答案 4 :(得分:2)
代码优化并不一定意味着必须省略调试符号。使用代码优化进行调试构建,然后您仍然可以调试程序,并且可能现在发生错误。
答案 5 :(得分:2)
一件容易的事情是启用编译器警告和提示,重建项目,然后修复所有警告/提示
干杯
答案 6 :(得分:2)
如果是Delphi商业代码,有数据存储组件等,则可能不适用以下内容。
然而,我正在编写有点计算机器视觉代码。大多数单元测试都是基于控制台的。我也参与了FPC,多年来用FPC测试了很多。部分出于爱好,部分处于绝望的情况下,我想要任何预感。我尝试过的一些标准技巧(降低实用性)
2和3几乎可以让你找到大多数(如果不是全部)初始化问题。
尝试找到任何线索,然后返回Delphi并搜索更集中,调试等。
我确实意识到这并不容易。我有很多FPC经验,并且没有必要从头开始查找这些案例。仍然值得一试,并且可能是开始设置非视觉系统和单元测试FPC兼容和平台独立的动机。无论如何,看到德尔福路线图,大部分工作都是必需的。
答案 7 :(得分:1)
在这些问题中,我总是建议使用日志文件。
问题:您能否以某种方式确定源代码中的错误显示?
如果没有,我的回答不会帮助你。
如果是,请检查是否存在错误,并在找到错误后立即将堆栈转储到日志文件中。 (有关转储和重新符号化堆栈的详细信息,请参阅post mortem debugging。)
如果您发现某些数据已损坏,但您不知道如何发生这种情况,请提取一个执行此类有效性测试的函数(如果失败则记录日志),并从越来越多的地方调用此函数程序执行(即每次菜单调用后)。如果你多次重申这种方法,你很有可能找到问题。
答案 8 :(得分:1)
这是程序或函数中的局部变量吗?
如果是这样,那么它将存在于堆栈中,并将包含垃圾。根据执行路径和编译器设置,垃圾会发生变化,可能会使你的逻辑“超越边缘”。
- 的Jeroen
答案 9 :(得分:0)
鉴于你对这个问题的描述,我认为你没有使用优化器就可以获得未经初始化的数据,但是这些数据会因为优化而爆炸。