最近,我们的大项目开始在零处理未处理的部门。最近的代码似乎不包含任何可能的元素,因此它可能是影响旧代码的新数据集。问题是代码库非常大,并且在没有舒适调试访问的嵌入式设备上运行(调试是通过串行控制台上的许多printf()完成的,设备没有gdb,即使有,用调试符号编译的二进制文件不适合。
最可行的方法可能是找到所有除法运算(它们相对不频繁),并分析每个运算周围的代码,以查看是否有任何除数变量无人看守。
问题是如何在一个大的(~200个文件,一些大的)C ++项目中找到所有除法运算,或者,如果你更好地了解如何找到错误,请给它们。
额外信息:项目在嵌入式ARM9上运行,这是一个小型自定义Linux发行版,与Cygwin / Windows crosstools交叉编译,IDE是Eclipse,但也有Cygwin和各自的好东西。事情是项目是特定于硬件的,并且只有在满负荷运行时才会发生崩溃,所有必要的互连模块都处于活动状态。只有裸骨活动的受限“故障模式”不会创建它们。
答案 0 :(得分:8)
我认为最直接的步骤是尝试捕获未处理的异常并生成转储或printf堆栈信息或类似信息。
Take a look at this question或者只是在Google中搜索与您的特定环境中的异常捕获相关的信息。
顺便说一下,我认为这种划分可能是因为调用外部库而发生的,因此不能100%确定你只是通过改变你的代码来找到罪魁祸首
答案 1 :(得分:8)
如果我没记错的话,ARM9没有硬件鸿沟,因此无论何时必须执行除法,它都将在编译器进行的函数调用中实现。
查看您的工具集是否以与ARM's toolset does相同的方式实现除零处理(它可能至少与此类似)。如果是这样,您可以安装在问题发生时调用的处理程序,并且可以printf()
注册和堆栈,以便您可以确定问题发生的位置。一个可能的类似替代方案是你的小型Linux发行版正在抛出你可以捕获的信号。
我不确定你是如何获得除以零的信息,但如果是因为运行时正在吐出一条消息,那么你总是可以选择找出处理的地方在运行时中,并用您自己的更多信息性消息替换它。但是,我猜想有一种更“架构”的方式来运行代码(信号处理程序或ARM的技术)。
答案 2 :(得分:6)
使用自定义grep搜索查找所有分区不应该很难。您可以轻松地将该用法与C ++中/
和%
字符的其他用法区分开来。
此外,如果您知道要划分的内容,则可以对/
和%
运算符进行全局重载,以使__FILE__
和__LINE__
通知断言。如果使用makefile,那么在不触及代码的情况下将自定义操作符代码包含在所有链接文件中应该不难。
答案 3 :(得分:6)
对于此问题和未来问题,您应该以此为借口投资改善设备的调试能力。即使您无法进行实时调试,您也应该能够找到一种方法来生成并保存核心转储以进行事后调试(立即查明源或任何未处理的异常)。
答案 4 :(得分:2)
PC-Lint可能会有所帮助,就像Findbugs for C ++一样。这是一种商业产品,但有30退款保证。
答案 5 :(得分:2)
处理异常。
通常异常将被传递给包含导致异常和其他信息的地址的结构。您可能必须熟悉微控制器的数据表或RTOS手册。
答案 6 :(得分:1)
对gcc使用-save-temps并在生成的.s文件中找到相关的除法程序集。如果你很幸运,它将是一个相当独特的东西,甚至可能是函数调用。如果是函数调用,您可以使用弱链接用您自己的选中版本覆盖它。否则,在程序集中查找分区应该可以很好地了解它们在C / C ++代码中的位置,并且可以直接对它们进行检测。
答案 7 :(得分:1)
如果您有权访问异常处理程序例程,通常可以修改/覆盖除零异常处理程序。 在ARM的情况下,除法由库例程完成。当有零除数时,有一些机制可以通知用户代码。
请参阅http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4061.html
我建议提供__rt_raise(),如上页所述。
当除法例程检测到除以零时,将调用__ rt_raise(2,2)。 所以你可以打印LR寄存器。 然后使用addr2line将其与源行交叉#
答案 8 :(得分:0)
找到这些条件的唯一方法是通常的:
答案 9 :(得分:0)
异常已经将违规除以零代码的地址位置。当发生包括PC(程序计数器)在内的异常时,CPU保存寄存器内容。您的操作系统应该传递此信息(我假设您知道它是如何除以零)。打印地址并查看代码。如果您可以打印堆栈跟踪,那么解决起来会更容易。
另一个选择是检查版本控制软件在上一个已知工作版本和第一个非工作版本之间的差异。这应该为您提供一个有限的更改集,在其中搜索问题。