新闻  |   论坛  |   博客  |   在线研讨会
数据篡改该如何定位?(2)
鱼鹰谈单片机 | 2021-01-17 12:13:28    阅读:371   发布文章

如果我们按 Objects 访问的话,那么下面的每一条语句都会导致程序运行的停止。

13.jpg

这是因为这些数据都在 Osprey 结构体的范围内(从这里也可以了解到,只要在 len 的范围内的访问都会导致程序停止运行,所以你可以试试将 Size 设置得更大)。

而如果设置为 Byte 访问的话,那么就只有第一条语句才会导致程序停止运行:

12.jpg

实际上如果你希望只在某个结构体成员变量被访问时才停止,那么直接这么设置就可以:

11.jpg

你会发现设置是如此之简单。

实际上还有一种更为通用的访问方式,即按地址访问。

上面可以看出 Ospery.Ospery1 成员变量的地址为 0x20000016(由此我们知道也可以通过这个来看出一个结构体变量的地址是多少)。所以我们可以这样设置:

10.jpg

而代码位置的断点设置亦是如此。

断点太多,怎么知道程序因何停止?看你的命令窗口就知道了:

9.jpg

3、数据匹配

有些时候,我们并不关注地址访问情况,而对变量的数据内容感兴趣。比如说鱼鹰想让变量emOspery 等于 1 时停下来,怎么设置?

8.jpg

只要简单的设置 emOspery == 1 即可(注意必须设置访问条件,并且 Size 设置正确)。

事实上你也可以设置两个变量相等作为条件:

7.jpg

设置为不等也是可以的:

6.jpg

当然还有其它支持的运算就靠你们自己去发现了(可支持运算:&,&&,<,<=,>,>= ,==,!=)。

注意:以上内容可以组合使用,比如读、写条件,计数器计数等可以同时设置。满足条件时就会让程序运行停止。

高级用法

以上为比较常规的调试功能,现在说说鱼鹰刚学习的技能,这个技能的使用灵活性更大,而且对于解决疑难杂症更是不二之选。

首先设置一个你需要的断点:

5.jpg

打开断点窗口,并双击你之前设置的断点:

4.jpg

设置 Command 为【printf(“USRAT_Init()\n”)】(注意\n,否则可能不能输出,这个应该是 KEIL 的一个 bug)。最后【Define】

清空你之前的命令(如果你不嫌乱的话,也可以不清空):

3.jpg

那么你的程序每次运行到这个代码位置都会在Command 窗口输出一条信息:

2.jpg

但是你的程序并不会停止。

如果说你想让断点代码位置运行多次之后才输出一条信息也是可以的,只要设置Count 即可。

这里可能你会问,这 printf 不就是我们写的打印函数吗?事实上,是,也不是。

这个函数是打印函数没错,但是这是 KEIL 调用的打印函数,输出位置是 Command 窗口,和你自己写的代码没一点关系,每次触发条件时KEIL 都会调用该函数进行打印,而不会让你的程序暂停运行。事实上这个 Command 绝不仅仅只是设置 printf这么简单,如果真是这样我也不会如此推崇它了,感兴趣的可以去官网查找关于调试命令的使用方法。

因为是利用 KEIL 去执行打印任务,所以对你的程序几乎没有任何影响,并且在你设置断点后也不用担心删除代码问题,可以放心饮用。还有一个额外的好处就是,对于所有能设置调试断点的单片机都适用,因此对于调试器也就没有过多的要求了,比如说,不管你是用JLINK、ST-LINK 还是CMSIS-DAP(CMSIS-DAP 不能使用ITM,所以鱼鹰才会想着用别的方式替代。总算是找到了,而且它在某些方面更出色),都可以这么用。

现在摘录官网一些关于断点窗口的知识:

表达式定义断点类型:

§当设置标志 Read 或 Write 或两者时,访问中断(A)被定义 。发生指定的内存访问时会触发断点。以字节为单位指定内存访问窗口的大小,或者以表达式的对象大小指定。对于此断点类型,Expression 必须解析为内存地址和内存类型。允许的运算符(&,&&,<。<=。>,> =,= =和!=)在程序执行暂停或执行命令之前比较变量值 。

§当 Expression 解析为代码地址时,将执行执行中断(E)。到达指定的代码地址时触发断点。代码地址必须引用 CPU 指令的第一个字节。

§当 Expression 不能简化为地址时,定义条件中断(C)。当条件表达式变为 TRUE 时,断点将触发。在每条 CPU 指令之后重新计算条件表达式,并且会大大减慢程序执行速度。

该计数值指定的次数的断点表达式必须计算为 TRUE 断点触发之前的数目。

当命令被指定的μVision 执行语句,然后恢复执行程序。此处指定的命令可以是μVision 调试或信号功能。要从这些函数中暂停程序执行,请设置系统变量 _break_。

注意

当在模拟器中将访问断点(读或写)设置为外设寄存器(SFR)时,即使应用程序未访问外设寄存器,断点也可能触发。发生这种情况是因为μVision 模拟器在应用程序驱动和模拟器内部访问之间没有区别。

里面有一个比较关键的就是关于条件中断(C),如果你设置的表达式不是一个代码地址,也没有设置读写访问条件,那么就会被设置为条件中断,一旦设置为条件中断,那么会在每条汇编指令后计算表达式,这会影响程序正常运行速度,所以没有必要的话,不要设置为条件中断。

设置断点的一般错误总结:

当弹出以下窗口时,说明断点设置错误,需要查看命令窗口才能知道具体错误信息。

1.jpg

a)      断点太多

删除一些断点即可

b)     重复定义断点

这是因为之前你已经定义了这个断点,而现在你又定义了这个断点,这个时候你可以选择覆盖之前的断点或者保留之前的断点

c)      不允许对同一个资源设置不同类型断点

这个是由于对同一个资源准备设置不同断点导致的,需要删除之前的设置的断点才行。

d)     表达式错误

检查你的表达式是否正确,注意如果你使用了运算符,那么对于浮点变量的支持好像并不正常,不管你怎么设置,都说表达式错误。

到此,断点窗口(前期我叫它数据观察点,我也不知道从哪看到的这个词,后来觉得还是断点窗口比较准确)的内容就结束了。这个小节内容对于调试而言绝对是一大利器,也是鱼鹰决定写这个KEIL 调试系列文章的主要原因。但是以上所有的调试内容都有一个很大的局限性,就是它只能定格在某一刻(如果你使用Command 命令就不一样了),而这一刻前面的所有信息都无法知晓。这个时候就要了解另一个调试技能,ITM,它能将程序从出生(复位程序开始)到死亡(死循环或者断电)的大部分信息记录下来。这个章节内容早已发布,感兴趣的就去前面看一看咯。 

2019-04-09  Osprey

更新2019-04-20  Osprey


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客