在进行嵌入式工程调试的时候,我们经常需要面对TASKING优化等级的调整,以及在代码被优化后,变量没办法在调试器里正常观察的情况,这两个问题其实挺常见的。遇到这种现象,大家先别急着觉得是调试器出了故障,或者以为变量真的凭空消失了,其实当优化功能被开启之后,编译器就会自动把变量放进寄存器里,或者把一些表达式进行合并,甚至还会把一些觉得没用的赋值给删掉,或者把函数直接做内联处理,这就会导致我们手写的源码执行顺序,和芯片最后实际执行的机器指令顺序对不上了,因此,在调试窗口里看的时候,就很容出现变量找不到、数值乱跳、断点定不准,还有单步执行顺序看起来乱七八糟的这些情况。
一、TASKING优化等级的调整方法
关于TASKING的优化等级,我们一般是在工程的属性界面里面去进行修改的,大家最好不要直接去改那些中间生成的配置文件,因为如果IDE重新生成了工程配置,我们之前手动改的内容就很容易被冲掉,导致修改失效。
1、怎么找到编译器的优化设置
在软件里,大家一般可以顺着这个路径去点:【Project】→【Properties】→【C/C++Build】→【Settings】→【Tool Settings】→【C/C++Compiler】→【Optimization】。因为TASKING的版本不一样,界面上的名字可能会有一点点差别,但基本上都在Compiler下面的Optimization或者是Code Generation这两个相关的页面里面。
2、对几种常见优化等级的粗浅理解
TASKING编译器一般是通过--optimize或者-O这两个指令来控制优化等级的;如果我们没有去写这个参数,系统默认通常都会使用--optimize=2,也就是Optimize more这一档。而如果用--optimize=0的话,编译器基本上就不会去做什么常规的优化,它会尽量让生成的机器代码和我们写的源码长得差不多,这样我们在做源码级调试的时候就会轻松很多。
3、调试信息需要同步把它打开
不过,就算我们把优化等级调低了,变量也不一定就能在窗口里显示出来,我们还得去确认一下工程里有没有把调试信息给使能。在TASKING里面,是通过--debug-info或者-g这个选项来生成符号调试信息的,如果我们没有勾选这个地方,编译器就根本不会往文件里写调试信息,那我们在看源码级变量的时候自然就什么都瞧不见了。
二、TASKING优化后变量无法观察的解决办法
要是碰到了变量没办法观察的情况,大家千万不要只傻傻地盯着Watch窗口发呆,我们得先试着去判断一下,这个变量到底是被编译器给优化掉了,还是说被塞进了寄存器里,或者是它的生命周期和作用域已经走完了,再或者,也有可能是当前的ELF文件和我们手里的源码对不上号。
1、先排查是不是高优化引起的
如果在Debug配置下面变量是能看到的,一换到Release配置下面就看不到了,那我们基本上就可以先怀疑是优化在捣鬼了。因为高的优化等级会把代码的结构给打乱,导致局部变量可能没有办法分配到固定的内存地址上,有一些临时变量也会直接被表达式给代替了,甚至连没用到的变量都会被直接删干净,我们在调试这种优化过后的C/C++程序时,变量找不到、单步调试乱跳行、断点立不稳这些事,其实都是挺普遍的。
2、去看一下变量还有没有真的存储位置
有很多变量,虽然我们在源码里老老实实地写出来了,但是编译之后它在内存里不一定能分到独立的地址。比方说,那些只用来参与了一次计算的局部变量、循环体里面的临时变量,还有被内联函数给吸收掉的那些参数,这些东西一般都没法稳定地在窗口里观察到。
3、确认一下调试文件是不是被精简了
如果ELF文件里面的调试信息在后续处理中被弄掉了,那调试器肯定是没办法把源码变量正确显示出来的。在TASKING的工具链里面,其实也塞了一些用来处理ELF文件、查看HLL符号或者是剥离调试段的工具;如果在大家的自动化构建流程里跑了strip或者类似的脚本,调试信息可能就已经从最后的文件里被剔除出去了。
三、让TASKING优化调试配置更稳定的几点建议
优化和调试这两个东西,在天生上其实就是一对矛盾,调试的时候我们希望源码和机器码能一行对一行地讲清楚,而优化的时候我们又希望代码能跑得更快、体积更小,所以我们在做工程的时候,不能只一味地去要求“所有变量都得看得到”,也不能盲目地觉得“优化等级越高越好”。
1、把Debug和Release的策略分开来搞
对于Debug配置,我个人建议把调试信息留着、把优化等级降下来、把那些没必要的全局优化给关掉,这样我们在定断点、跑单步和看变量的时候会舒服很多。而Release配置就根据产品的实际要求去堆优化等级就行,把重心放在运行效率、代码占用的空间、时序表现还有最后的成品验证上。
2、对有Bug的文件可以单独把优化降下来
如果因为各种原因,整个工程必须得保持很高的优化等级,但现在又正好有一个模块在出问题、急需排查,那我们其实可以只针对这一个C文件或者某一段局部代码把优化等级降下来。TASKING是支持通过单文件的编译选项来控制优化的,而且它也支持我们在源文件里写#pragma optimize这一类的语法,来强行覆盖掉一小段代码的优化设置。
3、多配合map文件和反汇编来做判断
在变量看不见的时候,大家千万别把所有的希望都寄托在Watch窗口上面,我们应该学着把map文件、反汇编、寄存器窗口还有内存窗口都结合起来一起看。TASKING在Eclipse工程里跑完编译之后生成的map文件,通常都会待在当前构建配置的输出目录下面,也就是Debug或者Release文件夹里。
总结
总的来说,想要解决TASKING优化等级怎么调整,以及TASKING优化后变量无法观察怎么办的问题,核心的办法就是把优化等级、调试信息、构建配置还有手里那份实际的ELF文件合在一起去过一遍。如果是那些必须开高优化的项目,我们可以只对出问题的那块文件临时降低优化,再配合着map文件、反汇编和寄存器窗口去抓原因。按照这几步勉强理出来的思路去弄,基本上就能在保证调试能看得见的同时,又不会把产品最终的优化配置给搞得乱七八糟了。