debugging 如何可视化AVR程序的内存(SRAM)使用情况?

kgsdhlau  于 2023-01-13  发布在  其他
关注(0)|答案(8)|浏览(220)

我在AVR微控制器(ATMega 328 P)上运行的C程序中遇到了一个问题。我认为这是由于堆栈/堆冲突造成的,但我希望能够确认这一点。
有什么方法可以让我看到堆栈和堆对SRAM的使用情况吗?

**注:**程序是用avr-gcc编译的,使用avr-libc。
**更新:**我遇到的实际问题是malloc实现失败(返回NULL)。所有malloc ing发生在启动时,所有free ing发生在应用程序结束时(实际上从来没有,因为应用程序的主要部分处于无限循环中)。所以我确信碎片不是问题。

chhkpiq4

chhkpiq41#

您可以使用avr-size实用程序检查RAM静态使用情况,如中所述
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=62968
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82536
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=95638
http://letsmakerobots.com/node/27115
avr-size -C -x Filename.elf
(avr大小的文档:http://ccrma.stanford.edu/planetccrma/man/man1/avr-size.1.html
下面是如何在IDE上设置此设置的示例:在Code::Blocks上,项目-〉生成选项-〉生成前/生成后步骤-〉生成后步骤包括:
avr-size -C $(TARGET_OUTPUT_FILE)
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)
构建结束时的输出示例:

AVR Memory Usage
----------------
Device: atmega16

Program:    7376 bytes (45.0% Full)
(.text + .data + .bootloader)

Data:         81 bytes (7.9% Full)
(.data + .bss + .noinit)

EEPROM:       63 bytes (12.3% Full)
(.eeprom)

数据是SRAM的使用量,它只是编译器在编译时所知道的数量,你还需要为运行时创建的东西(特别是堆栈的使用)留出空间。
检查堆栈使用情况(动态RAM),从http://jeelabs.org/2011/05/22/atmega-memory-use/
下面是一个小的实用函数,它可以确定当前未使用的RAM数量:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

下面是使用该代码的草图:

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

freeRam()函数返回堆的末尾和堆栈上最后分配的内存之间存在的字节数,因此它实际上是堆栈/堆在冲突之前可以增长多少。
您可以在您怀疑可能导致堆栈/堆冲突的代码周围检查此函数的返回。

ulydmbyx

ulydmbyx2#

你说malloc失败并返回NULL:
您应该首先查看的明显原因是您的堆"已满"-即,您请求malloc的内存无法分配,因为它不可用。
有两种情况需要牢记:
你有一个16K的堆,你已经分配了10K,你试图再分配10K,你的堆太小了。
b:更常见的情况是,你有一个16k的堆,你已经做了一堆malloc/free/realloc调用,你的堆还不到50% "满":你为1K调用malloc,它失败了--怎么回事?回答--堆的空闲空间被分割了--没有一个连续的1K空闲内存可以被返回。当这种情况发生时,C堆管理器不能压缩堆,所以你通常处于一个糟糕的状态。有一些技术可以避免分割,但很难知道这是否是真正的问题所在,您需要向malloc和free添加日志填充,以便了解正在执行的动态内存操作。
编辑:
您说所有的mallocs都发生在启动时,所以碎片不是问题。
在这种情况下,用静态分配代替动态分配应该很容易。
旧代码示例:

char *buffer;

void init()
{
  buffer = malloc(BUFFSIZE);
}

新代码:

char buffer[BUFFSIZE];

一旦你在所有地方都这样做了,如果所有东西都不能放入可用内存,你的LINKER会警告你。不要忘记减少堆的大小--但是要注意一些运行时io系统函数可能仍然使用堆,所以你可能无法完全删除它。

llew8vvj

llew8vvj3#

不要在较小的嵌入式目标上使用堆/动态分配,尤其是在处理器资源如此有限的情况下。相反,重新设计您的应用程序,因为随着程序的增长,问题会再次出现。

bnlyeluc

bnlyeluc4#

通常的方法是用已知的模式填充内存,然后检查哪些区域被覆盖。

jaql4c8m

jaql4c8m5#

如果你同时使用堆栈和堆,那么它可能会有点棘手。我将解释当不使用堆时我所做的事情。作为一个普遍的规则,我工作过的所有公司(在嵌入式C软件领域)都避免在小型嵌入式项目中使用堆a-以避免堆内存可用性的不确定性。我们使用静态声明的变量代替。
一种方法是用已知图案填充大部分堆叠区域(例如0x 55)。这通常是由软件执行早期的一小段代码完成的,或者是在main的开头(),或者甚至可以在main之前()在启动代码中开始。当然,注意不要覆盖此时正在使用的少量堆栈。然后,在运行软件一段时间后,检查堆栈空间的内容,看看0x 55在哪里仍然是完整的。2你如何“检查”取决于你的目标硬件。3假设你有一个调试器连接,那么你可以简单地停止微运行和读取内存。
如果您有可以执行内存访问断点的调试器(比通常的执行断点更花哨一点),那么你就可以在一个特定的堆栈位置设置断点--比如你的堆栈空间的最远极限。这是非常有用的,因为它还显示了当代码达到堆栈使用的程度时,究竟是哪一部分代码在运行。但是它需要调试器支持内存-访问断点特性,而且在“低端”调试器中通常找不到它。
如果您还使用heap,那么它可能会有点复杂,因为可能无法预测堆栈和heap将在何处发生冲突。

t30tvxxf

t30tvxxf6#

假设你只使用一个栈(所以不是RTOS或其他什么),栈在内存的末尾,向下增长,而堆在BSS/DATA区域之后开始增长,我见过malloc的实现实际上检查栈指针,并在冲突时失败。你可以尝试这样做。
如果您不能适应malloc代码,您可以选择将堆栈放在内存的起始位置(使用链接器文件)。一般来说,知道/定义栈的最大大小总是一个好主意。如果你把它放在栈的开头,读取RAM开头以外的内容时将出错。堆将位于末尾,如果出现以下情况,则可能无法超出末尾。It“这是一个不错的实现(将返回NULL)。好的方面是你知道有2个独立的错误案例对应2个独立的问题。
为了找出最大堆栈大小,你可以用一个模式填充你的内存,运行应用程序,看看它走了多远,也见克雷格的回复。

jrcvhitl

jrcvhitl7#

如果你可以编辑堆的代码,你可以在每个内存块上填充几个额外的字节(在这样低的资源上很棘手)。这些字节可以包含一个不同于堆栈的已知模式。如果它与堆栈冲突,通过看到它出现在堆栈内部,或者反之亦然,这可能会给予你一个线索。

3npbholx

3npbholx8#

在Unix类操作系统中,一个名为sbrk()的库函数(参数为0)允许你访问动态分配的堆内存的最高地址,返回值是一个void * 指针,可以与任意一个栈分配变量的地址进行比较。
使用此比较的结果时应谨慎。根据CPU和系统架构的不同,堆栈可能会从任意高地址向下增长,而分配的堆将从内存下限向上移动。
有时操作系统有其他的内存管理概念(例如OS/9),它把堆和栈放在空闲内存的不同内存段中。在这些操作系统上,尤其是嵌入式系统上,你需要预先定义应用程序的最大内存需求,使系统能够分配大小匹配的内存段。

相关问题