JNH官网

【ARM】MDK的断点的高级使用方法

一、文档背景

工程调试对于嵌入式开发来说是一个十分常见的工作。在调试的工作中,工程师通常为了去判断一个变量是否达到一个特定的值或者希望判断某一个数据或者地址是否被读写,而去编写对应的测试代码在调试中去方便判断。这样的做法会增加代码的内存体积还会影响调试速度,所以就希望通过其他的方法可以在避免产生这样的影响的同时又可以实现工程师的调试需求。


二、 问题分析

在进行调试过程中,断点调试应该是调试中最基础的功能,也是最常用的功能。工程师通常通过设置断点的方式去判断对应语句是否被执行,去判断对应的判断条件是否合理等情况。但是这样使用断点的方式属于最基础的使用方式。只是使用了执行断点,没有使用到条件断点和内存访问断点这个两种断点的高级使用方式。那么基于前面说的问题,就可以通过设置条件断点和内存访问断点的形式去实现。


三、 如何设置条件断点和内存访问断点

1、Breakpoints 窗口:是在Debug模式下设置断点和显示所有断点列表的一个窗口。进入Debug模式后,可以通过在菜单栏中Debug->Breakpoints...去打开Breakpoints窗口(如图3-1)。

图3-1


2、Breakpoints窗口包含的如下几个内容(如图3-2):

1)、Current Breakpoints:列举当前被定义的所有断点的信息。通过勾选前面的复选框可以使能失能断点。双击断点信息的情况下可以查看到具体的断点的配置信息。里面包含的断点信息有:

(1)、001:断点的序号。对于断点进行编号。

(2)、(E):表示断点的类型,E表示执行断点;A表示内存访问断点;C表示条件断点。具体三种断点的区别请查看MDK在debug模式下断点的类型

(3)、0x004009DE:表示设置了断点的语句的内存地址。

(4)、“\BlinkyBlinky.c47”:表示断点设置的位置,这里表示断点设置在Blinky工程中Blinky.c文件的47行。

2)、Expression:指定触发断点的表达式。这里可以填写语句的内存地址在语句处去设置断点。或者填写设置断点触发的条件表达式。

3)、count:指定在触发断点之前表达式必须为真的次数。执行断点中就是指定对应语句被执行的次数,默认情况下是1。内存访问断点就是指定对应变量被读写的次数。条件断点就指定对应的条件表达式为真的次数。

4)、command:断点被触发后想要执行的命令。可以是printf语句等

5)、Access:内存访问断点的设置

(1)、Read:定义在发生内存读访问时触发的访问中断。

(2)、Write:定义在发生内存写访问时触发的访问中断。

(3)、Size:指定检查读或写访问中断的内存大小。

(4)、Bytes:以字节为单位定义内存大小。

(5)、Objects:将内存大小定义为对象。

6)、操作按钮

(1)、Define:更新或创建一个断点。该操作取决于断点类型。当通过Breakpoint窗口去设置断点的时候,通过Define按钮去使能配置。

(2)、Kill selected:移除对应的断点。双击断点列表中的断点信息,然后点击Kill selected就可以删除对应的断点。

(3)、Kill All:移除全部断点

(4)、Close:关闭窗口

图3-2


3、如何通过Breakpoints窗口设置断点

1)、当JNH官网需要在某一个内存地址或者某个语句打断点的情况。除了直接在编辑窗口中直接打断点外,可以在Expression中输入对应的地址,然后点击Define后,就可以设置一个最基础的执行断点。(如图3-3-1))

图3-3-1


2)、如果想要设置条件断点,则就在Access中去勾选Write。那么对于变量写的时候就会中断的。这里需要注意条件断点只能是针对变量设置,不可以针对语句进行设置。(如图3-3-2)。

图3-3-2

关于表达式的书写按照正常的C语言条件表达式的语言规范去书写即可。符号选用&, &&, <, <=, >, >=, ==, !=即可。


3)、如果想要设置内存访问断点,则就在Access中去勾选Read或者Write或者都勾选。去配置是在读取的时候中断还是写入的时候中断还是只要发送读取或者写入就中断。这里需要注意内存访问断点只能是针对变量设置,不可以针对语句进行设置(如图3-3-3)。

图3-3-3


四、断点的高级用法示例

1、通过语句在工程和文件中的位置,在对应语句处设置执行断点。

1)、打开Breakpoint窗口后,在Expression中去输入对应语句的路径,例如:“\BlinkyBlinky.c38”,\Blinky是工程名,Blinky.c是文件名,38是语句在Blinky.c中的位置。编写好对应位置后,点击Define后,就可以设置对应的执行断点。(如图4-1-1)

图4-1-1


2)、配置好后,就可以在列表中看到对应的断点信息,也可以在文件中看到对应语句上设置了一个新的断点。(如图4-1-2)

图4-1-2


2、通过语句的内存位置,对对应的语句设置断点。

1)、想要通过语句的内存位置去设置断点,首先需要在Disassembly窗口中去确认对应语句的内存位置。那么在Dissassembly窗口中,可以看到对应语句的起始地址,比如图4-2-1中,test_num++语句的起始地址是0x00400c8a。

图4-2-1


2)、确认语句的内存地址后,就通过Breakpoint窗口去设置断点,在Expression窗口中输入在上一步中确认的语句的内存地址。然后点击Define去设置断点。(如图4-2-2)

图4-2-2


3)、配置好后,就可以在列表中看到对应的断点信息,也可以在文件中和Disassembly窗口中看到对应语句上设置了一个新的断点。(如图4-2-3)

图4-2-3


3、设置当test_num++语句后,打印出test_num的数值。

1)、首先,请参考上一步在Breakpoint窗口中的Expression中输入语句的位置或者内存地址。然后,想要设置执行这个语句的时候就输出test_num的值,那么就在Command中输入“printf("test_num == %dn",test_num)”。这里在输入的时候,需要注意双引号需要用”“进行转义,才能正常输出。输入后,就点击Define进行设置。(如图4-3-1)

图4-3-1


2)、设置好后,在运行的过程中,这个断点在运行中就不会停下来了。每次执行到这个断点的时候,就会直接去执行对应的命令,也就是JNH官网上面输入的打印语句命令。(如图4-3-2)

图4-3-2


4、设置当test_num++语句执行10次后,再执行对应的语句。

1)、按照上一步填写好语句的位置和命令。然后,count设置为10;(如图4-4-1)

图4-4-1


2)、那么就可以在Command窗口中看到,当语句被执行到第10次,也就test_num=9的时候执行对应的命令。(如图4-4-2)

图4-4-2


3)、那么不配置命令的情况,在执行过程中就会在语句被执行第10次的时候会在对应的断点处停下来。


5、设置test_num变量在被写入的时候,程序停止运行

1)、需要对于变量设置内存访问断点,首先需要将对应的变量添加到Watch窗口中,然后,选中对应的变量,右键选择Set Access Breakpoint at ‘xxx’。(如图4-5-1)


图4-5-1


2)、点击Set Access Breakpoint at ‘xxx’后就会打开Breakpoint窗口,然后,在Access中勾选上Write,表示在该变量在写入的时候暂停。也可以设置Count,表示在变量第几次写入的时候暂停。(如图4-5-2)

图4-5-2


3)、配置好后,那么当变量test_num被写入的时候程序就会停止下来。


6、设置当变量test_num等于10的时候程序停止运行

1)、如第5部分中一样,通过Watch窗口设置对应的变量。进入到Breakpoint窗口后,因为是要当变量等于10的时候程序暂停,那么这个过程中就要监测变量的写入情况,所以要勾选是Write。然后将原先Expression中的\BlinkyBlinky.cmaintest_num删掉,改成表达式“test_num == 10”,然后Define,配置条件断点。(如图4-6-1)



图4-6-1


2)、那么,程序就会在当test_num=10的时候停止。


五、讨论分析

1. 条件断点的设置对于仿真器的是否有要求?

文档中的第4章第5和6步,在SAMV71和ULINK pro的配置下无法设置。而使用stm32f7和ST-Link可以设置。

2. 执行断点的设置的时候命令的书写有没有上面限制?命令的书写规范?

对于表达式的书写,需要注意一些符号的转义。来确认可以正确读取表达式。

3. 内存访问断点设置触发条件为读写的时候,怎么判断是读触发了还是写触发了?

可以添加表达式来判断值的改变,值发生了改变可以初步判断为写触发,值没有发生改变可以判断为读触发。


六、结论

通过以上讲述的方法,可以减少因为需要对于某些语句的判断或者变量值的变化情况的检测,而在工程中去添加额外的测试代码,导致工程内存变大,编译速度也变慢的情况。并且可以在Debug模式下,随意地去对于工程代码进行调试和测试。

jnh官网 jnh官网 jnh官网 jnh官网 金年会 金年会 金年会 金年会