如何调试写入变量的 amok 运行 指针?
How to debug an amok running pointer writing to my variable?
用 C11 编写的嵌入式微控制器软件正在发生疯狂的事情。 (是的,不是 C++,只有 C)总之,我有一个 static
变量,它只写在整个软件的两个地方。由于我不应该在这里 post 数千行代码,我将总结写入变量的代码:
static index foo = 0;
define MAX_FOO 10
void bar_timer() {
printf("foo:%d\n", foo);
foo++;
if (foo >= MAX_FOO)
foo=0;
}
需要说明的是,该软件看起来确实不像上面的简单截图那么简单。 foo
实际上是静态结构数组中的一个索引 (int),该数组包含整数、浮点数和指向使用 malloc 保留的区域的浮点指针。然而,printf
的输出是这样的:
foo: 0
foo: 2
foo: 4
foo: 6
foo: 8
foo: 0
foo: 2
...
因为索引 foo
是数组的一部分,所以我想出了一个主意并增加了该数组,这会导致疯狂的副作用,例如软件非常慢或在开始时挂起。
我还尝试在 foo++
之前放置一个 printf(Before %d, foo)
,在 foo++
之后放置一个 printf(After %d, foo)
,结果是:
Before 0
After 1
Before 2
After 3
...
删除 foo++
最终得到:
Before 0
After 0
Before 0
After 0
...
我可以打赌有某种疯狂的 运行 指针(隐藏在数千行代码中某处的软件错误中的任何地方)只会增加我的变量背后的存储值 foo
.
如何调试 amok 运行 指针写入我的变量?
首先,static
和 malloc 没有意义,您的变量不能同时具有静态存储和分配存储 - 它必须具有两者之一。
How to debug amok running pointer writing to my variable?
取决于目标。仅在现代 MCU 上可用的最佳方法是在内存位置使用硬件写断点,然后当断点命中时检查硬件指令跟踪以找到罪魁祸首。您还需要一个具有跟踪支持的像样的调试器。
如果具备以上所有条件,则查找错误大约需要 5 到 10 分钟。与使用常规调试的 hours/days/weeks 相反。所以这绝对是选择MCU和工具链时要考虑的事情。
假设您没有现代零件和设备,那么一些在线调试器可能具有实时更新监视变量的能力。这可能会提供线索,特别是如果您禁用应该写入变量的代码,然后发现它仍然写入。
获得完全随机行为时最有可能的罪魁祸首是堆栈溢出。 static
变量存储在 .bss
/.data
(或 .heap
PC 程序员编码嵌入式系统......)但一个常见的初学者错误是内存映射堆栈,以便它溢出到 RAM 的其他部分,例如 .bss
。这是您应该排除的第一件事。
检查堆栈溢出的常用技巧是在调试器中启动程序,将整个堆栈区域设置为已知值,如 0xAA
,然后让程序 运行 运行一段时间并冻结它。检查堆栈所在的内存区域,看看它已经消耗了多少 0xAA
单元格。
如果不是堆栈溢出而是某些损坏的指针或数组越界,则首先检查链接器生成的映射文件。您的变量附近是否有大型缓冲区或数组?
万一你怀疑超过运行s,你可以制作自己的“金丝雀”,类似于上面用0xAA填充堆栈。在损坏的变量上方或下方声明一些虚拟 volatile 变量,看看它们是否也发生变化。
另一个可能的罪魁祸首是竞争条件。如果 bar_timer()
从 ISR 执行,但 static
变量在 ISR 和 main() 之间共享。不用说,您也不应该从 ISR 内部调用像 stdio.h 这样的膨胀函数。
当其他一切都失败时,您必须求助于单步执行程序,直到 bug 首次出现为止。
最后,避免此类错误的最好方法是首先不编写它们。这是通过严格使用编码标准来实现的。例如你提到malloc
,它不应该在微控制器应用程序中使用,主要是因为使用它没有丝毫意义。但是,如果您仍然使用它,您还会遇到常见的 PC 编程问题,例如内存泄漏、堆损坏和碎片化等。
用 C11 编写的嵌入式微控制器软件正在发生疯狂的事情。 (是的,不是 C++,只有 C)总之,我有一个 static
变量,它只写在整个软件的两个地方。由于我不应该在这里 post 数千行代码,我将总结写入变量的代码:
static index foo = 0;
define MAX_FOO 10
void bar_timer() {
printf("foo:%d\n", foo);
foo++;
if (foo >= MAX_FOO)
foo=0;
}
需要说明的是,该软件看起来确实不像上面的简单截图那么简单。 foo
实际上是静态结构数组中的一个索引 (int),该数组包含整数、浮点数和指向使用 malloc 保留的区域的浮点指针。然而,printf
的输出是这样的:
foo: 0
foo: 2
foo: 4
foo: 6
foo: 8
foo: 0
foo: 2
...
因为索引 foo
是数组的一部分,所以我想出了一个主意并增加了该数组,这会导致疯狂的副作用,例如软件非常慢或在开始时挂起。
我还尝试在 foo++
之前放置一个 printf(Before %d, foo)
,在 foo++
之后放置一个 printf(After %d, foo)
,结果是:
Before 0
After 1
Before 2
After 3
...
删除 foo++
最终得到:
Before 0
After 0
Before 0
After 0
...
我可以打赌有某种疯狂的 运行 指针(隐藏在数千行代码中某处的软件错误中的任何地方)只会增加我的变量背后的存储值 foo
.
如何调试 amok 运行 指针写入我的变量?
首先,static
和 malloc 没有意义,您的变量不能同时具有静态存储和分配存储 - 它必须具有两者之一。
How to debug amok running pointer writing to my variable?
取决于目标。仅在现代 MCU 上可用的最佳方法是在内存位置使用硬件写断点,然后当断点命中时检查硬件指令跟踪以找到罪魁祸首。您还需要一个具有跟踪支持的像样的调试器。
如果具备以上所有条件,则查找错误大约需要 5 到 10 分钟。与使用常规调试的 hours/days/weeks 相反。所以这绝对是选择MCU和工具链时要考虑的事情。
假设您没有现代零件和设备,那么一些在线调试器可能具有实时更新监视变量的能力。这可能会提供线索,特别是如果您禁用应该写入变量的代码,然后发现它仍然写入。
获得完全随机行为时最有可能的罪魁祸首是堆栈溢出。 static
变量存储在 .bss
/.data
(或 .heap
PC 程序员编码嵌入式系统......)但一个常见的初学者错误是内存映射堆栈,以便它溢出到 RAM 的其他部分,例如 .bss
。这是您应该排除的第一件事。
检查堆栈溢出的常用技巧是在调试器中启动程序,将整个堆栈区域设置为已知值,如 0xAA
,然后让程序 运行 运行一段时间并冻结它。检查堆栈所在的内存区域,看看它已经消耗了多少 0xAA
单元格。
如果不是堆栈溢出而是某些损坏的指针或数组越界,则首先检查链接器生成的映射文件。您的变量附近是否有大型缓冲区或数组?
万一你怀疑超过运行s,你可以制作自己的“金丝雀”,类似于上面用0xAA填充堆栈。在损坏的变量上方或下方声明一些虚拟 volatile 变量,看看它们是否也发生变化。
另一个可能的罪魁祸首是竞争条件。如果 bar_timer()
从 ISR 执行,但 static
变量在 ISR 和 main() 之间共享。不用说,您也不应该从 ISR 内部调用像 stdio.h 这样的膨胀函数。
当其他一切都失败时,您必须求助于单步执行程序,直到 bug 首次出现为止。
最后,避免此类错误的最好方法是首先不编写它们。这是通过严格使用编码标准来实现的。例如你提到malloc
,它不应该在微控制器应用程序中使用,主要是因为使用它没有丝毫意义。但是,如果您仍然使用它,您还会遇到常见的 PC 编程问题,例如内存泄漏、堆损坏和碎片化等。