IAR Embedded Workbench编译工程时,可以针对Debug和Release分别设置代码优化级别。优化打开后,编译器可能删除没有实际用途的局部变量、把变量暂存在寄存器中、调整语句执行顺序,或者直接把函数展开到调用位置,所以C-SPY里看到的变量状态可能和源码不完全对应。常见表现包括变量显示Unavailable、局部变量突然消失、单步执行顺序跳动以及断点无法停在某一行。碰到这些现象时,要先确认工程实际使用的优化配置,再判断是调试信息显示变化,还是程序运行结果真的出了问题。
一、IAR怎么配置代码优化级别
IAR的优化设置可以作用于整个工程,也可以单独覆盖某个Group或源文件。调试阶段和发布阶段可以使用不同配置,不必为了查一个函数把整个工程长期放在同一优化级别。
1、设置整个工程的优化级别
①在Workspace上方确认当前使用的是【Debug】还是【Release】配置。
②选中工程名称,右键打开【Options】。
③在【Category】中选择【C/C++Compiler】。
④进入【Optimizations】页面。
⑤在【Optimization level】中选择需要的级别。
⑥查变量、单步执行和断点问题时,可以先改成【None】。
⑦需要保留一定优化时,可切换到【Low】或【Medium】测试。
⑧使用【High】时,还要选择【Balanced】【Size】或【Speed】策略。
⑨点击【OK】保存,再执行【Project】→【Rebuild All】。
优化级别改变后要重新编译源码,不能只重新进入C-SPY。旧目标文件如果没有更新,调试器看到的仍可能是之前的优化结果。
2、只降低某个文件的优化级别
工程较大时,可以只关闭正在排查的源码文件优化。
①在Workspace中找到目标.c或.cpp文件。
②右键该文件,选择【Options】。
③进入【C/C++Compiler】→【Optimizations】。
④取消继承工程配置后,把【Optimization level】设置为【None】。
⑤点击【OK】保存。
⑥重新编译这个文件或执行【Rebuild All】。
⑦进入C-SPY,再查看该文件中的局部变量和断点。
文件级配置会覆盖工程级配置。后面把工程从Debug切到Release时,也要留意这个文件是否仍保留单独的优化设置,否则可能出现其他文件已经使用Release参数,只有它还在用调试配置。
3、检查调试信息有没有生成
①进入【Project】→【Options】。
②选择【C/C++Compiler】→【Output】。
③检查【Generate debug information】是否开启。
④保存配置后重新编译。
⑤启动【Download and Debug】。
⑥打开【View】→【Locals】查看当前函数局部变量。
⑦需要长期观察某个变量时,打开【Watch】窗口并添加对应表达式。
⑧全局变量或静态变量可以继续通过【Statics】或【Watch】检查。
只降低优化级别但没有调试信息,C-SPY仍然无法正常显示源码变量,所以这两项配置要一起核对。
二、IAR开启优化后调试变量显示异常如何解决
优化后的变量显示异常,很多时候只是变量在当前指令位置没有可供C-SPY读取的存储位置。先用None做一次对照测试,可以很快判断变量是真被程序改坏了,还是只在调试窗口里暂时看不到。
1、变量显示Unavailable时先做对照测试
①记录当前工程的【Optimization level】。
②把出现问题的源文件单独改成【None】。
③执行【Rebuild All】。
④重新进入C-SPY。
⑤在变量赋值语句前后设置断点。
⑥打开【Locals】和【Watch】。
⑦单步执行到变量实际参与运算的位置,再观察数值。
⑧如果关闭优化后变量能够连续显示,而打开优化后出现【Unavailable】,可以把排查方向放到编译器优化带来的调试信息变化上。
局部变量声明后并不一定马上占用一个固定内存位置。编译器判断某个阶段还不需要这个变量时,C-SPY就可能暂时拿不到它的值。
2、单步执行顺序跳动时查看Step Point
①进入优化后的调试会话。
②观察编辑器左侧可以设置断点的位置。
③把断点放在语句起始位置或函数调用附近。
④不要要求每一条源码语句都能逐行停住。
⑤执行【Step Over】时,结合当前PC位置观察代码跳转。
⑥怀疑源码和机器指令对应不上时,打开【Disassembly】。
⑦同时查看源码与汇编指令的对应关系。
⑧切换到【None】重新编译一次,对比单步执行变化。
优化会合并、移动甚至删除部分语句对应的机器指令,所以源码看起来有好几行,并不代表调试时一定能一行接一行停下来。
3、局部变量消失或数值变化很快时检查优化动作
①打开【Project】→【Options】→【C/C++Compiler】→【Optimizations】。
②查看当前【Enabled transformations】。
③检查是否启用了【Function inlining】。
④再检查【Common subexpression elimination】。
⑤查看【Code motion】和【Loop unrolling】等当前可用选项。
⑥为了定位某一类调试问题,可以暂时关闭对应转换。
⑦重新编译后再次进入C-SPY。
⑧确认变量显示和断点位置是否发生变化。
例如函数被内联后,原函数调用位置和栈帧结构会改变;代码移动后,某个表达式真正执行的位置也可能和源码顺序不同。
三、调试时需要优化又要观察变量怎么处理
有些问题只有打开优化后才会出现,这时不能一直使用None调试。更合适的做法是保留问题出现时的编译条件,只对目标文件或目标优化动作做小范围调整。
1、保留原优化级别逐项缩小范围
①保留工程当前【Medium】或【High】设置。
②只给异常源码文件设置单独的编译选项。
③先关闭【Function inlining】重新测试。
④问题仍存在时,再检查【Code motion】。
⑤涉及循环时查看【Loop unrolling】。
⑥每修改一项都重新编译目标文件。
⑦记录变量显示和程序行为有没有变化。
⑧找到影响调试观察的项目后,再决定调试版本是否保留该调整。
这样可以保住大部分工程原来的编译状态,同时把检查范围缩到一个文件或一种转换上。
2、确认是不是代码本身依赖了错误写法
①检查异常变量是否被中断和主程序同时访问。
②异步变化的变量如果确实需要每次从内存读取,确认源码有没有按实际用途使用【volatile】。
③检查指针类型转换和内存别名用法。
④高优化下才出现运行结果错误时,查看【Type-based alias analysis】相关设置。
⑤暂时关闭对应转换重新测试。
⑥关闭后程序恢复时,再回头检查源码里的指针访问是否符合当前编译规则。
⑦不要为了让Watch窗口一直显示数值,就随意给普通局部变量添加volatile。
调试窗口显示异常和程序运行异常要分开处理。前者可能只是变量被优化掉,后者如果只在特定优化级别下出现,就要继续查源码里的内存访问、异步变量和类型使用方式。
总结
IAR开启代码优化后,源码和最终机器指令之间的对应关系会发生变化,C-SPY里的变量、断点和单步结果也可能随之改变。调试时可以保留一份低优化配置,同时针对问题文件单独调整编译选项;如果运行结果本身也随优化级别变化,则应继续检查源码中的内存访问和变量使用方式,而不能只把问题归到调试器显示上。如需进一步了解IAR代码优化配置、C-SPY变量调试与优化后运行异常排查方法,欢迎联系咨询。