当程序突然无法写入Flash,或者发现RAM的占用量比上一个版本明显增加的时候,IAR生成的map文件往往比编译窗口里的信息更管用;大家会关心在IAR的map文件里哪些信息最关键,分析的时候又该先去看哪几项占用,正确的做法并不是从头一行行翻看,先把内存区域和汇总结果弄清楚,再回过头去细查模块、段和符号,排查速度就会快上很多。IAR的连接器负责把代码和数据安排到目标内存中,而map文件就是用来查看连接之后各部分摆放情况的一份记录。
一、IAR map文件里哪些信息最关键
因为芯片系列、连接器版本和工程设置的不同,IAR生成的map文件内容会存在一些差别,但分析的顺序基本差不多,我们需要重点关注内存区域的划分、段的放置位置、各个模块的贡献大小,还有符号对应的地址。
1、先看内存区域范围
打开map文件以后,先要确认ROM和RAM的起始地址、结束地址、已经用掉的空间以及还剩下多少空间;这些数字要跟芯片手册还有icf连接配置文件里的设置对应得上。如果连接的时候提示空间不足,就去判断到底是整个区域真的被塞满了,还是因为某个段没办法放进它指定的区域里,这样才能找准方向。
2、查看段放置结果
接着去检查.text、.rodata、.data、.bss、.noinit以及堆和栈都放到了哪里;.text段通常对应程序代码,.rodata一般是只读的常量,.data用来保存那些需要初始化的读写数据,.bss则保存启动时被清零的数据。按照IAR文档的说明,带初始值的静态变量会在RAM里占地方,同时还需要在ROM中保留一份初始化内容,这些情况会同时影响两头的占用。
3、查看模块占用汇总
要是map文件里存在模块汇总的部分,就可以按模块去查看它们各自贡献了多少代码、只读数据和读写数据;每当加进某个新功能后发现占用突然涨上去了,先在这里找出增长明显的目标文件或库文件,然后再回到源码里核查,效率会更高。IAR的资料也说明,模块汇总的作用就是列出每个模块对总内存使用量贡献了多少。
4、查看符号和地址
当需要追查某个具体的函数或全局变量时,再去搜索符号名称;符号列表很适合用来核实函数的地址、变量的地址以及自定义段被放到了哪里。如果发现某个数组占的空间实在太大,或者函数被错误地安排到了不正确的区域,这些蛛丝马迹通常都能从符号和段的信息当中找出来。
二、IAR map文件分析时该先看哪几项占用
在分析占用情况的时候,比较合理的做法是先把ROM和RAM分开来看,因为两者增长的原因往往不一样,不能一看到总量变大了就马上动手删代码。
1、先看ROM里的代码占用
ROM的占用量增长时,要先看.text段和那些只读的数据;常见的原因有新加的功能函数、格式化输出、浮点运算、字符串表、查找表,还有一些被连带进来的库函数。如果自己只是添加了少量业务代码,ROM却涨得很显眼,那就要检查是不是在无意中间接引用到了体积较大的库模块。
2、再看RAM里的静态数据
轮到RAM部分,就要盯紧.data、.bss和.noinit这几个段;大数组、缓存区、通信队列、图像缓冲以及全局结构体,这些都很容易吃下不少空间。而且带初始值的全局变量还会额外占用ROM里的初始化内容,所以有时候改动一处数组,ROM和RAM两边的占用量可能都会跟着发生变化。
3、单独检查堆和栈
堆和栈的空间一般都是在icf文件里提前预留好的,不能光看当前程序有没有用到它们;栈空间要是留得太小,运行时可能发生溢出,而堆设得太大,又会挤占到别的RAM区域。好在IAR支持开启栈使用分析功能,启用之后map文件里就会多出一个栈使用的章节,也可以生成调用图文件来帮助做出判断。
4、留意初始化数据聚合
在使用了压缩初始化内容的情况下,一些带_init后缀的段并不会单独列出来,而是被合并成initializer bytes这么一项;当你发现ROM的占用数字跟手工把各项加起来的结果对不上时,就要去检查这一部分,别想当然地以为是map文件漏算了什么东西。
三、IAR map文件占用异常怎么继续排查
对map文件做完分析以后,最好不要只单独留下一份结果,真正有用的是把前后两个版本的文件摆在一起进行对比,这样才能确认占用的增长到底来自哪一个模块、哪一个段或者哪一个符号。
1、保留基线版本
每次完成正式发布的时候,记得把elf文件、map文件、icf文件和构建日志都一块儿保存下来;等到新版本出现占用异常,就先拿ROM、RAM、堆和栈这些指标去跟基线比较,然后再去比对模块汇总的部分,看看差异出在了什么地方。
2、核对构建配置
调试版本和发布版本、不同级别的优化、不一样的宏定义,还有有差异的库配置,这些因素都会改变最终的结果;所以在做对比之前,一定要先确认两次的工程配置是保持一致的,否则两组数字之间根本就没有直接的可比性。
3、从模块回查符号
一旦发现某个模块的占用突然增长了,就可以再到符号列表里面去搜索具体的函数、数组和常量表;这样先把排查的范围缩小,然后再进到源码里处理,比在整个工程当中漫无目的地瞎猜要省事得多。
总结起来看,分析IAR的map文件可以依照“先看内存区域、再看段放置、接着查模块汇总、然后对符号地址、最后留意堆栈预留”这一套顺序来进行;在查看ROM的时候重点放在代码和只读数据上面,查看RAM的时候则要重点关注静态变量、各类缓存、堆和栈。如果遇到占用突然增长的情况,只要把前后两个版本的map文件保留下来做对比,很快就能看出变化落在了哪一个模块。